From a6d380862fdeeddbd21c33561309b1544d1cd3d5 Mon Sep 17 00:00:00 2001 From: RenatoDiGiacomo Date: Thu, 21 Jul 2022 10:19:27 -0300 Subject: [PATCH 1/9] Title Reactive Done --- .../Base/MainContainer/MainContainer.jsx | 1 - .../src/components/Menu/MenuComponent.jsx | 2 +- .../src/components/PageTitle/PageTitle.jsx | 45 ++++++++++++++++++- .../src/components/UserModal/UserModal.jsx | 25 +++++++---- frontend/src/layout/index.js | 6 +-- 5 files changed, 63 insertions(+), 16 deletions(-) diff --git a/frontend/src/components/Base/MainContainer/MainContainer.jsx b/frontend/src/components/Base/MainContainer/MainContainer.jsx index b8bb60d..e38be30 100644 --- a/frontend/src/components/Base/MainContainer/MainContainer.jsx +++ b/frontend/src/components/Base/MainContainer/MainContainer.jsx @@ -1,7 +1,6 @@ import React from "react"; import PageTitle from "../../PageTitle/PageTitle"; - import MainContainerStyled, { TitleContainerStyled, ContentContainerStyled, diff --git a/frontend/src/components/Menu/MenuComponent.jsx b/frontend/src/components/Menu/MenuComponent.jsx index 047aedc..dc0c6c9 100644 --- a/frontend/src/components/Menu/MenuComponent.jsx +++ b/frontend/src/components/Menu/MenuComponent.jsx @@ -63,7 +63,7 @@ const MenuComponent = () => { } text="Tickets" to="/tickets" hover={hover} /> } text="Contatos" to="/contacts" hover={hover} /> } text="Lembretes" to="/schedulesReminder" hover={hover} /> - } text="Respostas" to="" hover={hover} /> + } text="Respostas" to="/quickAnswers" hover={hover} /> } text="Usuários" to="/users" hover={hover} /> } text="Filas" to="/Queues" hover={hover} /> diff --git a/frontend/src/components/PageTitle/PageTitle.jsx b/frontend/src/components/PageTitle/PageTitle.jsx index 6ba57c1..364db61 100644 --- a/frontend/src/components/PageTitle/PageTitle.jsx +++ b/frontend/src/components/PageTitle/PageTitle.jsx @@ -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,57 @@ 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); + + console.log(path.pathname); + + 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 ( -

PageTitle

+

{title}

); diff --git a/frontend/src/components/UserModal/UserModal.jsx b/frontend/src/components/UserModal/UserModal.jsx index 3f9776c..eb254c6 100644 --- a/frontend/src/components/UserModal/UserModal.jsx +++ b/frontend/src/components/UserModal/UserModal.jsx @@ -15,12 +15,9 @@ import UserImg from "../../assets/images/User/user.jpg"; const UserModal = ({ modal, click }) => { const { user } = React.useContext(AuthContext); + const InitalState = { name: user.name, email: user.email, profile: user.profile }; + const [userData, setUserData] = React.useState(InitalState); - const initalData = { - name: user.name, - email: user.email, - perfil:user.profile, - } console.log(user); return ( @@ -28,8 +25,20 @@ const UserModal = ({ modal, click }) => { - - + setUserData({name:event.target.data})} + /> + setUserData({email:event.target.data})} + /> @@ -53,4 +62,4 @@ id: 2 name: "teste" profile: "master" queues: [] -tokenVersion: 0 */ \ No newline at end of file +tokenVersion: 0 */ diff --git a/frontend/src/layout/index.js b/frontend/src/layout/index.js index 353899e..dcc26ee 100644 --- a/frontend/src/layout/index.js +++ b/frontend/src/layout/index.js @@ -8,11 +8,7 @@ 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); + if (loading) { return ; From 87e0d61281cbe87f5c177850522a56e2c0ded403 Mon Sep 17 00:00:00 2001 From: RenatoDiGiacomo Date: Thu, 21 Jul 2022 13:58:50 -0300 Subject: [PATCH 2/9] New Changes in login and title menu. needed fix the reactive actions in user profile --- .../Base/MainContainer/MainContainer.style.jsx | 4 ++++ frontend/src/components/PageTitle/PageTitle.jsx | 9 +++------ frontend/src/components/PageTitle/UserBtn/UserBtn.jsx | 2 +- frontend/src/layout/index.js | 3 +-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/Base/MainContainer/MainContainer.style.jsx b/frontend/src/components/Base/MainContainer/MainContainer.style.jsx index a288e71..ac76b22 100644 --- a/frontend/src/components/Base/MainContainer/MainContainer.style.jsx +++ b/frontend/src/components/Base/MainContainer/MainContainer.style.jsx @@ -1,4 +1,5 @@ import styled from "styled-components"; +import { color } from "../../../style/varibles"; const MainContainerStyled = styled.main` padding-left: 97px; @@ -21,6 +22,9 @@ const ContentContainerStyled = styled.div` height: 100vh; overflow: hidden; margin-top: 16px; + border-radius: 5px; + background-color: ${color.complement.azulOscuro}; + padding: 6px; `; export default MainContainerStyled; diff --git a/frontend/src/components/PageTitle/PageTitle.jsx b/frontend/src/components/PageTitle/PageTitle.jsx index 364db61..9ead028 100644 --- a/frontend/src/components/PageTitle/PageTitle.jsx +++ b/frontend/src/components/PageTitle/PageTitle.jsx @@ -9,14 +9,11 @@ import logo from "../../assets/images/Logo.png"; const PageTitle = () => { const path = useLocation(); - const [title,setTitle] = React.useState() + const [title, setTitle] = React.useState(); const [modal, setModal] = React.useState(false); const { user } = React.useContext(AuthContext); - - console.log(path.pathname); - - React.useEffect(()=>{ + React.useEffect(() => { switch (path.pathname) { case "/tickets": setTitle("Tickets"); @@ -51,7 +48,7 @@ const PageTitle = () => { default: setTitle("Dashboard"); } - },[path]) + }, [path]); const handleModal = () => { setModal(!modal); diff --git a/frontend/src/components/PageTitle/UserBtn/UserBtn.jsx b/frontend/src/components/PageTitle/UserBtn/UserBtn.jsx index 299a22e..6bc2e14 100644 --- a/frontend/src/components/PageTitle/UserBtn/UserBtn.jsx +++ b/frontend/src/components/PageTitle/UserBtn/UserBtn.jsx @@ -34,7 +34,7 @@ const UserBtn = ({ user, img, modal, modalSet }) => { } onClick={handleModal} /> - + Deseja Sair do sistema? diff --git a/frontend/src/layout/index.js b/frontend/src/layout/index.js index dcc26ee..2dd0ea9 100644 --- a/frontend/src/layout/index.js +++ b/frontend/src/layout/index.js @@ -7,8 +7,7 @@ import { i18n } from "../translate/i18n"; import MenuComponent from "../components/Menu/MenuComponent"; const LoggedInLayout = ({ children }) => { - const { handleLogout, loading, isAuth } = React.useContext(AuthContext); - + const { loading, user } = React.useContext(AuthContext); if (loading) { return ; From a66355f43bebf8c8ab39a37d3069674b3da43185 Mon Sep 17 00:00:00 2001 From: RenatoDiGiacomo Date: Thu, 21 Jul 2022 14:00:06 -0300 Subject: [PATCH 3/9] Tickets in progress I needed understand more about Socket.IO and how this aplication work to update de visual --- frontend/src/components/Ticket/index.js | 2 - frontend/src/components/TicketsList/index.js | 504 +++++++++--------- .../TicketsManager/TicketsManager.jsx | 338 ++++++++++++ .../src/components/TicketsManager/index.js | 4 +- .../components/TicketsQueueSelect/index.js | 93 ++-- .../src/components/UserModal/UserModal.jsx | 36 +- frontend/src/pages/Tickets/Tickets.jsx | 20 + frontend/src/pages/Tickets/Tickets.style.jsx | 9 + frontend/src/routes/index.js | 2 +- 9 files changed, 683 insertions(+), 325 deletions(-) create mode 100644 frontend/src/components/TicketsManager/TicketsManager.jsx create mode 100644 frontend/src/pages/Tickets/Tickets.jsx create mode 100644 frontend/src/pages/Tickets/Tickets.style.jsx diff --git a/frontend/src/components/Ticket/index.js b/frontend/src/components/Ticket/index.js index 6b4a1cb..004803c 100644 --- a/frontend/src/components/Ticket/index.js +++ b/frontend/src/components/Ticket/index.js @@ -91,8 +91,6 @@ const Ticket = () => { const fetchTicket = async () => { try { - // maria julia - const { data } = await api.get("/tickets/" + ticketId); // setContact(data.contact); diff --git a/frontend/src/components/TicketsList/index.js b/frontend/src/components/TicketsList/index.js index 1fb3263..604d603 100644 --- a/frontend/src/components/TicketsList/index.js +++ b/frontend/src/components/TicketsList/index.js @@ -12,320 +12,294 @@ import useTickets from "../../hooks/useTickets"; import { i18n } from "../../translate/i18n"; import { AuthContext } from "../../context/Auth/AuthContext"; -const useStyles = makeStyles(theme => ({ - ticketsListWrapper: { - position: "relative", - display: "flex", - height: "100%", - flexDirection: "column", - overflow: "hidden", - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, +const useStyles = makeStyles((theme) => ({ + ticketsListWrapper: { + position: "relative", + display: "flex", + height: "100%", + flexDirection: "column", + overflow: "hidden", + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + }, - ticketsList: { - flex: 1, - overflowY: "scroll", - ...theme.scrollbarStyles, - borderTop: "2px solid rgba(0, 0, 0, 0.12)", - }, + ticketsList: { + flex: 1, + overflowY: "scroll", + ...theme.scrollbarStyles, + borderTop: "2px solid rgba(0, 0, 0, 0.12)", + }, - ticketsListHeader: { - color: "rgb(67, 83, 105)", - zIndex: 2, - backgroundColor: "white", - borderBottom: "1px solid rgba(0, 0, 0, 0.12)", - display: "flex", - alignItems: "center", - justifyContent: "space-between", - }, + ticketsListHeader: { + color: "rgb(67, 83, 105)", + zIndex: 2, + backgroundColor: "white", + borderBottom: "1px solid rgba(0, 0, 0, 0.12)", + display: "flex", + alignItems: "center", + justifyContent: "space-between", + }, - ticketsCount: { - fontWeight: "normal", - color: "rgb(104, 121, 146)", - marginLeft: "8px", - fontSize: "14px", - }, + ticketsCount: { + fontWeight: "normal", + color: "rgb(104, 121, 146)", + marginLeft: "8px", + fontSize: "14px", + }, - noTicketsText: { - textAlign: "center", - color: "rgb(104, 121, 146)", - fontSize: "14px", - lineHeight: "1.4", - }, + noTicketsText: { + textAlign: "center", + color: "rgb(104, 121, 146)", + fontSize: "14px", + lineHeight: "1.4", + }, - noTicketsTitle: { - textAlign: "center", - fontSize: "16px", - fontWeight: "600", - margin: "0px", - }, + noTicketsTitle: { + textAlign: "center", + fontSize: "16px", + fontWeight: "600", + margin: "0px", + }, - noTicketsDiv: { - display: "flex", - height: "100px", - margin: 40, - flexDirection: "column", - alignItems: "center", - justifyContent: "center", - }, + noTicketsDiv: { + display: "flex", + height: "100px", + margin: 40, + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + }, })); const reducer = (state, action) => { - if (action.type === "LOAD_TICKETS") { - const newTickets = action.payload; - - - newTickets.forEach(ticket => { + if (action.type === "LOAD_TICKETS") { + const newTickets = action.payload; - // console.log('* ticket.unreadMessages: ',ticket.unreadMessages) + 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); + } + }); - 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]; + } - return [...state]; - } + if (action.type === "RESET_UNREAD") { + const ticketId = action.payload; - if (action.type === "RESET_UNREAD") { - const ticketId = action.payload; + const ticketIndex = state.findIndex((t) => t.id === ticketId); + if (ticketIndex !== -1) { + state[ticketIndex].unreadMessages = 0; + } - const ticketIndex = state.findIndex(t => t.id === ticketId); - if (ticketIndex !== -1) { - state[ticketIndex].unreadMessages = 0; - } + return [...state]; + } - return [...state]; - } + if (action.type === "UPDATE_TICKET") { + const ticket = action.payload; - 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); + } - // console.log('++++++++++++ UPDATE_TICKET: ',ticket) + return [...state]; + } - const ticketIndex = state.findIndex(t => t.id === ticket.id); - if (ticketIndex !== -1) { - state[ticketIndex] = ticket; - } else { - state.unshift(ticket); - } + if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") { + const message = action.payload.message; - return [...state]; - } + const ticket = action.payload.ticket; - if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") { + const ticketIndex = state.findIndex((t) => t.id === ticket.id); - const message = action.payload.message + if (ticketIndex !== -1) { + if (!message.fromMe) { + ticket.unreadMessages += 1; + } - const ticket = action.payload.ticket; + state[ticketIndex] = ticket; + state.unshift(state.splice(ticketIndex, 1)[0]); + } else { + state.unshift(ticket); + } - const ticketIndex = state.findIndex(t => t.id === ticket.id); + return [...state]; + } - if (ticketIndex !== -1) { - - // console.log('>>>>>> ticketIndex: ', ticketIndex) + 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]; + } - // console.log('&&&&&&& UPDATE_TICKET_UNREAD_MESSAGES ticket: ',ticket, ' |\n MESSAGE: ', message) - - if(!message.fromMe){ - ticket.unreadMessages +=1 - } + if (action.type === "DELETE_TICKET") { + const ticketId = action.payload; + const ticketIndex = state.findIndex((t) => t.id === ticketId); + if (ticketIndex !== -1) { + state.splice(ticketIndex, 1); + } - state[ticketIndex] = ticket; - state.unshift(state.splice(ticketIndex, 1)[0]); - - } else { - state.unshift(ticket); - } + return [...state]; + } - 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 []; - } + if (action.type === "RESET") { + return []; + } }; -const TicketsList = (props) => { - const { status, searchParam, showAll, selectedQueueIds, updateCount, style } = - props; - const classes = useStyles(); - const [pageNumber, setPageNumber] = useState(1); - const [ticketsList, dispatch] = useReducer(reducer, []); - const { user } = useContext(AuthContext); +const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount, style }) => { + const classes = useStyles(); + const [pageNumber, setPageNumber] = useState(1); + const [ticketsList, dispatch] = useReducer(reducer, []); + const { user } = useContext(AuthContext); - useEffect(() => { - dispatch({ type: "RESET" }); - setPageNumber(1); - }, [status, searchParam, dispatch, showAll, selectedQueueIds]); + useEffect(() => { + dispatch({ type: "RESET" }); + setPageNumber(1); + }, [status, searchParam, dispatch, showAll, selectedQueueIds]); - const { tickets, hasMore, loading } = useTickets({ - pageNumber, - searchParam, - status, - showAll, - queueIds: JSON.stringify(selectedQueueIds), - }); + const { tickets, hasMore, loading } = useTickets({ + pageNumber, + searchParam, + status, + showAll, + queueIds: JSON.stringify(selectedQueueIds), + }); - useEffect(() => { - if (!status && !searchParam) return; - dispatch({ - type: "LOAD_TICKETS", - payload: tickets, - }); - }, [tickets, status, searchParam]); + useEffect(() => { + if (!status && !searchParam) return; + dispatch({ + type: "LOAD_TICKETS", + payload: tickets, + }); + }, [tickets, status, searchParam]); - useEffect(() => { - const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + useEffect(() => { + const socket = openSocket(process.env.REACT_APP_BACKEND_URL); - const shouldUpdateTicket = ticket => - (!ticket.userId || ticket.userId === user?.id || showAll) && - (!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1); + const shouldUpdateTicket = (ticket) => + (!ticket.userId || ticket.userId === user?.id || showAll) && + (!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1); - const notBelongsToUserQueues = ticket => - ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1; + const notBelongsToUserQueues = (ticket) => + ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1; - socket.on("connect", () => { - if (status) { - socket.emit("joinTickets", status); - } else { - socket.emit("joinNotification"); - } + socket.on("connect", () => { + if (status) { + socket.emit("joinTickets", status); + } else { + socket.emit("joinNotification"); + } + }); - }); + socket.on("ticket", (data) => { + if (data.action === "updateUnread") { + dispatch({ + type: "RESET_UNREAD", + payload: data.ticketId, + }); + } - 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" && 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 === "update" && notBelongsToUserQueues(data.ticket)) { - dispatch({ type: "DELETE_TICKET", payload: data.ticket.id }); - } + if (data.action === "delete") { + dispatch({ type: "DELETE_TICKET", payload: data.ticketId }); + } + }); - if (data.action === "delete") { - dispatch({ type: "DELETE_TICKET", payload: data.ticketId }); - } - }); + socket.on("appMessage", (data) => { + if (data.action === "create" && shouldUpdateTicket(data.ticket)) { + dispatch({ + type: "UPDATE_TICKET_UNREAD_MESSAGES", + // payload: data.ticket, + payload: data, + }); + } + }); - socket.on("appMessage", data => { - if (data.action === "create" && shouldUpdateTicket(data.ticket)) { + socket.on("contact", (data) => { + if (data.action === "update") { + dispatch({ + type: "UPDATE_TICKET_CONTACT", + payload: data.contact, + }); + } + }); - // console.log('((((((((((((((((((( DATA.MESSAGE: ', data.message) + return () => { + socket.disconnect(); + }; + }, [status, showAll, user, selectedQueueIds]); - dispatch({ - type: "UPDATE_TICKET_UNREAD_MESSAGES", - // payload: data.ticket, - payload: data, - }); - } - }); + useEffect(() => { + if (typeof updateCount === "function") { + updateCount(ticketsList.length); + } + }, [ticketsList]); - socket.on("contact", data => { - if (data.action === "update") { - dispatch({ - type: "UPDATE_TICKET_CONTACT", - payload: data.contact, - }); - } - }); + const loadMore = () => { + setPageNumber((prevState) => prevState + 1); + }; - return () => { - socket.disconnect(); - }; - }, [status, showAll, user, selectedQueueIds]); + const handleScroll = (e) => { + if (!hasMore || loading) return; - useEffect(() => { + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + if (scrollHeight - (scrollTop + 100) < clientHeight) { + loadMore(); + } + }; - - if (typeof updateCount === "function") { - updateCount(ticketsList.length); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ticketsList]); - - const loadMore = () => { - setPageNumber(prevState => prevState + 1); - }; - - const handleScroll = e => { - if (!hasMore || loading) return; - - const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; - - if (scrollHeight - (scrollTop + 100) < clientHeight) { - loadMore(); - } - }; - - return ( - - - - {ticketsList.length === 0 && !loading ? ( -
- - {i18n.t("ticketsList.noTicketsTitle")} - -

- {i18n.t("ticketsList.noTicketsMessage")} -

-
- ) : ( - <> - {ticketsList.map(ticket => ( - - ))} - - )} - {loading && } -
-
-
- ); + return ( + + + + {ticketsList.length === 0 && !loading ? ( +
+ {i18n.t("ticketsList.noTicketsTitle")} +

{i18n.t("ticketsList.noTicketsMessage")}

+
+ ) : ( + <> + {ticketsList.map((ticket) => ( + + ))} + + )} + {loading && } +
+
+
+ ); }; export default TicketsList; + diff --git a/frontend/src/components/TicketsManager/TicketsManager.jsx b/frontend/src/components/TicketsManager/TicketsManager.jsx new file mode 100644 index 0000000..b1af5b8 --- /dev/null +++ b/frontend/src/components/TicketsManager/TicketsManager.jsx @@ -0,0 +1,338 @@ +import React, { useContext, useEffect, useRef, useState } from "react"; +import openSocket from "socket.io-client" + +import { makeStyles } from "@material-ui/core/styles"; +import Paper from "@material-ui/core/Paper"; +import SearchIcon from "@material-ui/icons/Search"; +import InputBase from "@material-ui/core/InputBase"; +import Tabs from "@material-ui/core/Tabs"; +import Tab from "@material-ui/core/Tab"; +import Badge from "@material-ui/core/Badge"; +import MoveToInboxIcon from "@material-ui/icons/MoveToInbox"; +import CheckBoxIcon from "@material-ui/icons/CheckBox"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import Switch from "@material-ui/core/Switch"; +import { Button } from "@material-ui/core"; + +import 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"; + +const useStyles = makeStyles((theme) => ({ + ticketsWrapper: { + position: "relative", + display: "flex", + height: "100%", + flexDirection: "column", + overflow: "hidden", + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + }, + + tabsHeader: { + flex: "none", + backgroundColor: "#eee", + }, + + settingsIcon: { + alignSelf: "center", + marginLeft: "auto", + padding: 8, + }, + + tab: { + minWidth: 120, + width: 120, + }, + + ticketOptionsBox: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + background: "#fafafa", + padding: theme.spacing(1), + }, + + serachInputWrapper: { + flex: 1, + background: "#fff", + display: "flex", + borderRadius: 40, + padding: 4, + marginRight: theme.spacing(1), + }, + + searchIcon: { + color: "grey", + marginLeft: 6, + marginRight: 6, + alignSelf: "center", + }, + + searchInput: { + flex: 1, + border: "none", + borderRadius: 30, + }, + + badge: { + right: "-10px", + }, + show: { + display: "block", + }, + hide: { + display: "none !important", + }, +})); + +const TicketsManager = () => { + const classes = useStyles(); + + // Old New State + const [newPage, setNewPage] = React.useState(true); + // Old New State + + const [searchParam, setSearchParam] = useState(""); + const [tab, setTab] = useState("open"); + const [tabOpen, setTabOpen] = useState("open"); + const [newTicketModalOpen, setNewTicketModalOpen] = useState(false); + const [showAllTickets, setShowAllTickets] = useState(false); + const searchInputRef = useRef(); + const { user } = useContext(AuthContext); + + const [openCount, setOpenCount] = useState(0); + const [pendingCount, setPendingCount] = useState(0); + + const userQueueIds = user.queues.map((q) => q.id); + const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []); + const socket = openSocket.open(process.env.REACT_APP_BACKEND_URL) + +console.log(user.queues) +console.log(selectedQueueIds) +socket.on("connect", () => { + if ("open") { + socket.emit("joinTickets", "open"); + } else { + socket.emit("joinNotification"); + } +}); + + +socket.on("connect", ()=>{ + +}) + + + useEffect(() => { + if (user.profile.toUpperCase() === "ADMIN") { + setShowAllTickets(true); + } + }, [user.profile]); + + useEffect(() => { + if (tab === "search") { + searchInputRef.current.focus(); + } + }, [tab]); + + let searchTimeout; + + const handleSearch = (e) => { + const searchedTerm = e.target.value.toLowerCase(); + + clearTimeout(searchTimeout); + + if (searchedTerm === "") { + setSearchParam(searchedTerm); + setTab("open"); + return; + } + + searchTimeout = setTimeout(() => { + setSearchParam(searchedTerm); + }, 500); + }; + + const handleChangeTab = (e, newValue) => { + setTab(newValue); + }; + + const handleChangeTabOpen = (e, newValue) => { + setTabOpen(newValue); + }; + + const applyPanelStyle = (status) => { + if (tabOpen !== status) { + return { width: 0, height: 0 }; + } + }; + + if (newPage) { + return ( + <> + setNewTicketModalOpen(false)} + /> +
+ setSelectedQueueIds(values)} + /> + setOpenCount(val)} + style={applyPanelStyle("open")} + /> + setPendingCount(val)} + style={applyPanelStyle("pending")} + /> +
+ + ); + } + return ( + + setNewTicketModalOpen(false)} + /> + + + } + label={i18n.t("tickets.tabs.open.title")} + classes={{ root: classes.tab }} + /> + } + label={i18n.t("tickets.tabs.closed.title")} + classes={{ root: classes.tab }} + /> + } + label={i18n.t("tickets.tabs.search.title")} + classes={{ root: classes.tab }} + /> + + + + {tab === "search" ? ( +
+ + +
+ ) : ( + <> + + ( + setShowAllTickets((prevState) => !prevState)} + name="showAllTickets" + color="primary" + /> + } + /> + )} + /> + + )} + setSelectedQueueIds(values)} + /> +
+ + + + {i18n.t("ticketsList.assignedHeader")} + + } + value={"open"} + /> + + {i18n.t("ticketsList.pendingHeader")} + + } + value={"pending"} + /> + + + setOpenCount(val)} + style={applyPanelStyle("open")} + /> + setPendingCount(val)} + style={applyPanelStyle("pending")} + /> + + + + + + + + +
+ ); +}; + +export default TicketsManager; diff --git a/frontend/src/components/TicketsManager/index.js b/frontend/src/components/TicketsManager/index.js index 88e6ea0..7a2ed26 100644 --- a/frontend/src/components/TicketsManager/index.js +++ b/frontend/src/components/TicketsManager/index.js @@ -9,9 +9,9 @@ import Tab from "@material-ui/core/Tab"; import Badge from "@material-ui/core/Badge"; import MoveToInboxIcon from "@material-ui/icons/MoveToInbox"; import CheckBoxIcon from "@material-ui/icons/CheckBox"; - import FormControlLabel from "@material-ui/core/FormControlLabel"; import Switch from "@material-ui/core/Switch"; +import { Button } from "@material-ui/core"; import NewTicketModal from "../NewTicketModal"; import TicketsList from "../TicketsList"; @@ -21,7 +21,6 @@ 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"; const useStyles = makeStyles((theme) => ({ ticketsWrapper: { @@ -112,7 +111,6 @@ const TicketsManager = () => { if (user.profile.toUpperCase() === "ADMIN") { setShowAllTickets(true); } - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { diff --git a/frontend/src/components/TicketsQueueSelect/index.js b/frontend/src/components/TicketsQueueSelect/index.js index 2d4d225..e3d1f09 100644 --- a/frontend/src/components/TicketsQueueSelect/index.js +++ b/frontend/src/components/TicketsQueueSelect/index.js @@ -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 ( -
- - - -
- ); + return ( +
+ + + +
+ ); }; export default TicketsQueueSelect; + diff --git a/frontend/src/components/UserModal/UserModal.jsx b/frontend/src/components/UserModal/UserModal.jsx index eb254c6..efa0c0f 100644 --- a/frontend/src/components/UserModal/UserModal.jsx +++ b/frontend/src/components/UserModal/UserModal.jsx @@ -13,12 +13,35 @@ 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 InitalState = { name: user.name, email: user.email, profile: user.profile }; - const [userData, setUserData] = React.useState(InitalState); - 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 ( @@ -30,14 +53,14 @@ const UserModal = ({ modal, click }) => { label="Nome" type="text" value={userData.name} - onChange={(event) => setUserData({name:event.target.data})} + onChange={(event) => setUserData({ name: event.target.data })} /> setUserData({email:event.target.data})} + onChange={(event) => setUserData({ email: event.target.data })} /> @@ -63,3 +86,4 @@ name: "teste" profile: "master" queues: [] tokenVersion: 0 */ + diff --git a/frontend/src/pages/Tickets/Tickets.jsx b/frontend/src/pages/Tickets/Tickets.jsx new file mode 100644 index 0000000..fa7eb4c --- /dev/null +++ b/frontend/src/pages/Tickets/Tickets.jsx @@ -0,0 +1,20 @@ +import React from "react"; +import { useParams } from "react-router-dom"; + +import TicketsStyled from "./Tickets.style"; + +import TicketsManager from "../../components/TicketsManager/TicketsManager"; +import Ticket from "../../components/Ticket/"; + +const Tickets = () => { + const { ticketId } = useParams(); + return ( + + + {ticketId ? :
Não tem nada
} +
+ ); +}; + +export default Tickets; + diff --git a/frontend/src/pages/Tickets/Tickets.style.jsx b/frontend/src/pages/Tickets/Tickets.style.jsx new file mode 100644 index 0000000..c8c3ea7 --- /dev/null +++ b/frontend/src/pages/Tickets/Tickets.style.jsx @@ -0,0 +1,9 @@ +import styled from "styled-components"; + +const TicketsStyled = styled.div` + display: flex; + flex-direction: row; + height: 86vh; +`; + +export default TicketsStyled \ No newline at end of file diff --git a/frontend/src/routes/index.js b/frontend/src/routes/index.js index 431140c..ac11b68 100644 --- a/frontend/src/routes/index.js +++ b/frontend/src/routes/index.js @@ -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"; From b5975993bebe0fe2f4f658df52ac4a916adb032b Mon Sep 17 00:00:00 2001 From: RenatoDiGiacomo Date: Mon, 25 Jul 2022 09:59:34 -0300 Subject: [PATCH 4/9] Left painel in construction , WIP --- frontend/src/components/MessagesList/index.js | 5 - frontend/src/components/Ticket/index.js | 68 +-- .../components/TicketActionButtons/index.js | 272 +++++----- .../TicketListItem/TicketListItem.jsx | 37 ++ .../src/components/TicketListItem/index.js | 10 +- .../components/TicketsList/TicketsList.jsx | 225 ++++++++ frontend/src/components/TicketsList/index.js | 504 +++++++++--------- .../TicketsManager/TicketsManager.jsx | 343 ++---------- .../TicketsManager/TicketsManager.style.jsx | 7 + frontend/src/pages/Tickets/Tickets.style.jsx | 3 +- 10 files changed, 707 insertions(+), 767 deletions(-) create mode 100644 frontend/src/components/TicketListItem/TicketListItem.jsx create mode 100644 frontend/src/components/TicketsList/TicketsList.jsx create mode 100644 frontend/src/components/TicketsManager/TicketsManager.style.jsx diff --git a/frontend/src/components/MessagesList/index.js b/frontend/src/components/MessagesList/index.js index 0344c7b..3eedb23 100644 --- a/frontend/src/components/MessagesList/index.js +++ b/frontend/src/components/MessagesList/index.js @@ -541,11 +541,6 @@ const MessagesList = ({ ticketId, isGroup }) => { })} >
- {!message.quotedMsg?.fromMe && ( - - {message.quotedMsg?.contact?.name} - - )} {message.quotedMsg?.body}
diff --git a/frontend/src/components/Ticket/index.js b/frontend/src/components/Ticket/index.js index 004803c..f569d73 100644 --- a/frontend/src/components/Ticket/index.js +++ b/frontend/src/components/Ticket/index.js @@ -19,64 +19,11 @@ import toastError from "../../errors/toastError"; const drawerWidth = 320; -const useStyles = makeStyles((theme) => ({ - root: { - display: "flex", - height: "100%", - position: "relative", - overflow: "hidden", - }, - - ticketInfo: { - maxWidth: "50%", - flexBasis: "50%", - [theme.breakpoints.down("sm")]: { - maxWidth: "80%", - flexBasis: "80%", - }, - }, - ticketActionButtons: { - maxWidth: "50%", - flexBasis: "50%", - display: "flex", - [theme.breakpoints.down("sm")]: { - maxWidth: "100%", - flexBasis: "100%", - marginBottom: "5px", - }, - }, - - mainWrapper: { - flex: 1, - height: "100%", - display: "flex", - flexDirection: "column", - overflow: "hidden", - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - borderLeft: "0", - marginRight: -drawerWidth, - transition: theme.transitions.create("margin", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - }, - - mainWrapperShift: { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - transition: theme.transitions.create("margin", { - easing: theme.transitions.easing.easeOut, - duration: theme.transitions.duration.enteringScreen, - }), - marginRight: 0, - }, -})); const Ticket = () => { const { ticketId } = useParams(); const history = useHistory(); - const classes = useStyles(); + const [drawerOpen, setDrawerOpen] = useState(false); const [loading, setLoading] = useState(true); @@ -93,9 +40,6 @@ const Ticket = () => { const { data } = await api.get("/tickets/" + ticketId); - // setContact(data.contact); - // setTicket(data); - setContact(data.contact.contact); setTicket(data.contact); @@ -153,23 +97,21 @@ const Ticket = () => { }; return ( -
+
-
+
-
+
diff --git a/frontend/src/components/TicketActionButtons/index.js b/frontend/src/components/TicketActionButtons/index.js index 3919503..58feff5 100644 --- a/frontend/src/components/TicketActionButtons/index.js +++ b/frontend/src/components/TicketActionButtons/index.js @@ -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( + + ); + }; - 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() - - }; - + 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 ( +
+ {ticket.status === "closed" && ( + } + size="small" + onClick={(e) => handleUpdateTicketStatus(e, "open", user?.id)} + > + {i18n.t("messagesList.header.buttons.reopen")} + + )} + {ticket.status === "open" && ( + <> + } + size="small" + onClick={(e) => handleUpdateTicketStatus(e, "pending", null)} + > + {i18n.t("messagesList.header.buttons.return")} + - await api.put(`/tickets/${ticket.id}`, { - status: status, - userId: userId || null, - schedulingNotifyData: JSON.stringify(schedulingData) - }); + { + handleModal(); + // handleUpdateTicketStatus(e, "closed", user?.id) + }} + > + {i18n.t("messagesList.header.buttons.resolve")} + - } - 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 ( -
- {ticket.status === "closed" && ( - } - size="small" - onClick={e => handleUpdateTicketStatus(e, "open", user?.id)} - > - {i18n.t("messagesList.header.buttons.reopen")} - - )} - {ticket.status === "open" && ( - <> - } - size="small" - onClick={e => handleUpdateTicketStatus(e, "pending", null)} - > - {i18n.t("messagesList.header.buttons.return")} - - - { - - - handleModal() - // handleUpdateTicketStatus(e, "closed", user?.id) - - }} - > - {i18n.t("messagesList.header.buttons.resolve")} - - - - - - - - )} - {ticket.status === "pending" && ( - handleUpdateTicketStatus(e, "open", user?.id)} - > - {i18n.t("messagesList.header.buttons.accept")} - - )} -
- ); + + + + + + )} + {ticket.status === "pending" && ( + handleUpdateTicketStatus(e, "open", user?.id)} + > + {i18n.t("messagesList.header.buttons.accept")} + + )} +
+ ); }; export default TicketActionButtons; + diff --git a/frontend/src/components/TicketListItem/TicketListItem.jsx b/frontend/src/components/TicketListItem/TicketListItem.jsx new file mode 100644 index 0000000..9673a1f --- /dev/null +++ b/frontend/src/components/TicketListItem/TicketListItem.jsx @@ -0,0 +1,37 @@ +import React from "react"; + +import { useHistory, useParams } from "react-router-dom"; +import { parseISO, format, isSameDay } from "date-fns"; + +import { i18n } from "../../translate/i18n"; + +import api from "../../services/api"; +import ButtonWithSpinner from "../ButtonWithSpinner"; +import MarkdownWrapper from "../MarkdownWrapper"; +import { Tooltip } from "@material-ui/core"; +import { AuthContext } from "../../context/Auth/AuthContext"; +import toastError from "../../errors/toastError"; + +const TicketListItem = ({ ticket, status }) => { + const history = useHistory(); + const { ticketId } = useParams(); + const isMounted = React.useRef(true); + const { user } = React.useContext(AuthContext); + + const handleSelectTicket = (id) => { + history.push(`/tickets/${id}`); + }; + console.log(ticket.id); + + if (ticket.status !== status) return null; + return ( + +

{ticket.id}

+ {ticket.id} +

{ticket.lastMessage}

+
+ ); +}; + +export default TicketListItem; + diff --git a/frontend/src/components/TicketListItem/index.js b/frontend/src/components/TicketListItem/index.js index f7460bf..5cc3008 100644 --- a/frontend/src/components/TicketListItem/index.js +++ b/frontend/src/components/TicketListItem/index.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef, useContext } from "react"; -import { useHistory, useParams } from "react-router-dom"; +import ReactDOM, { useHistory, useParams } from "react-router-dom"; import { parseISO, format, isSameDay } from "date-fns"; import clsx from "clsx"; @@ -102,10 +102,10 @@ const useStyles = makeStyles(theme => ({ })); const TicketListItem = ({ ticket }) => { - const classes = useStyles(); - const history = useHistory(); + const classes = ReactDOM.useStyles(); + const history = ReactDOM.useHistory(); const [loading, setLoading] = useState(false); - const { ticketId } = useParams(); + const { ticketId } = ReactDOM.useParams(); const isMounted = useRef(true); const { user } = useContext(AuthContext); @@ -145,7 +145,7 @@ const TicketListItem = ({ ticket }) => { if (ticket.status === "pending") return; handleSelectTicket(ticket.id); }} - selected={ticketId && +ticketId === ticket.id} + selected={ticketId && +ticketId === ticket.id} ///img src{id}.jpg className={clsx(classes.ticket, { [classes.pendingTicket]: ticket.status === "pending", })} diff --git a/frontend/src/components/TicketsList/TicketsList.jsx b/frontend/src/components/TicketsList/TicketsList.jsx new file mode 100644 index 0000000..31aede6 --- /dev/null +++ b/frontend/src/components/TicketsList/TicketsList.jsx @@ -0,0 +1,225 @@ +import React, { useReducer } from "react"; +import openSocket from "socket.io-client"; + +import useTickets from "../../hooks/useTickets"; +import TicketListItem from "../TicketListItem/TicketListItem"; +import TicketsListSkeleton from "../TicketsListSkeleton"; + +import { i18n } from "../../translate/i18n"; +import { AuthContext } from "../../context/Auth/AuthContext"; + +const reducer = (state, action) => { + if (action.type === "LOAD_TICKETS") { + const newTickets = action.payload; + + newTickets.forEach((ticket) => { + const ticketIndex = state.findIndex((t) => t.id === ticket.id); + if (ticketIndex !== -1) { + state[ticketIndex] = ticket; + if (ticket.unreadMessages > 0) { + state.unshift(state.splice(ticketIndex, 1)[0]); + } + } else { + state.push(ticket); + } + }); + + return [...state]; + } + + if (action.type === "RESET_UNREAD") { + const ticketId = action.payload; + + const ticketIndex = state.findIndex((t) => t.id === ticketId); + if (ticketIndex !== -1) { + state[ticketIndex].unreadMessages = 0; + } + + return [...state]; + } + + if (action.type === "UPDATE_TICKET") { + const ticket = action.payload; + + const ticketIndex = state.findIndex((t) => t.id === ticket.id); + if (ticketIndex !== -1) { + state[ticketIndex] = ticket; + } else { + state.unshift(ticket); + } + + return [...state]; + } + + if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") { + const message = action.payload.message; + + const ticket = action.payload.ticket; + + const ticketIndex = state.findIndex((t) => t.id === ticket.id); + + if (ticketIndex !== -1) { + if (!message.fromMe) { + ticket.unreadMessages += 1; + } + + state[ticketIndex] = ticket; + state.unshift(state.splice(ticketIndex, 1)[0]); + } else { + state.unshift(ticket); + } + + return [...state]; + } + + if (action.type === "UPDATE_TICKET_CONTACT") { + const contact = action.payload; + const ticketIndex = state.findIndex((t) => t.contactId === contact.id); + if (ticketIndex !== -1) { + state[ticketIndex].contact = contact; + } + return [...state]; + } + + if (action.type === "DELETE_TICKET") { + const ticketId = action.payload; + const ticketIndex = state.findIndex((t) => t.id === ticketId); + if (ticketIndex !== -1) { + state.splice(ticketIndex, 1); + } + + return [...state]; + } + + if (action.type === "RESET") { + return []; + } +}; + +const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount }) => { + const [pageNumber, setPageNumber] = React.useState(1); + const { user } = React.useContext(AuthContext); + const [ticketsList, dispatch] = useReducer(reducer, []); + + const { tickets, hasMore, loading } = useTickets({ + pageNumber, + searchParam, + status, + showAll, + queueIds: JSON.stringify(selectedQueueIds), + }); + + React.useEffect(() => { + dispatch({ type: "RESET" }); + setPageNumber(1); + }, [status, searchParam, dispatch, showAll, selectedQueueIds]); + + React.useEffect(() => { + if (!status && !searchParam) return; + dispatch({ + type: "LOAD_TICKETS", + payload: tickets, + }); + }, [tickets, status, searchParam]); + + React.useEffect(() => { + const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + + const shouldUpdateTicket = (ticket) => + (!ticket.userId || ticket.userId === user?.id || showAll) && + (!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1); + + const notBelongsToUserQueues = (ticket) => + ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1; + + socket.on("connect", () => { + if (status) { + socket.emit("joinTickets", status); + } else { + socket.emit("joinNotification"); + } + }); + + socket.on("ticket", (data) => { + if (data.action === "updateUnread") { + dispatch({ + type: "RESET_UNREAD", + payload: data.ticketId, + }); + } + + if (data.action === "update" && shouldUpdateTicket(data.ticket)) { + dispatch({ + type: "UPDATE_TICKET", + payload: data.ticket, + }); + } + + if (data.action === "update" && notBelongsToUserQueues(data.ticket)) { + dispatch({ type: "DELETE_TICKET", payload: data.ticket.id }); + } + + if (data.action === "delete") { + dispatch({ type: "DELETE_TICKET", payload: data.ticketId }); + } + }); + + socket.on("appMessage", (data) => { + if (data.action === "create" && shouldUpdateTicket(data.ticket)) { + dispatch({ + type: "UPDATE_TICKET_UNREAD_MESSAGES", + // payload: data.ticket, + payload: data, + }); + } + }); + + socket.on("contact", (data) => { + if (data.action === "update") { + dispatch({ + type: "UPDATE_TICKET_CONTACT", + payload: data.contact, + }); + } + }); + + return () => { + socket.disconnect(); + }; + }, [status, showAll, user, selectedQueueIds]); + + const loadMore = () => { + setPageNumber((prevState) => prevState + 1); + }; + + const handleScroll = (e) => { + if (!hasMore || loading) return; + + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + + if (scrollHeight - (scrollTop + 100) < clientHeight) { + loadMore(); + } + }; + + return ( +
    + {ticketsList.length === 0 && !loading ? ( +
    + {i18n.t("ticketsList.noTicketsTitle")} +

    {i18n.t("ticketsList.noTicketsMessage")}

    +
    + ) : ( + <> + {ticketsList.map((ticket) => ( + + ))} + + )} + {loading && } +
+ ); +}; + +export default TicketsList; + diff --git a/frontend/src/components/TicketsList/index.js b/frontend/src/components/TicketsList/index.js index 604d603..1fb3263 100644 --- a/frontend/src/components/TicketsList/index.js +++ b/frontend/src/components/TicketsList/index.js @@ -12,294 +12,320 @@ import useTickets from "../../hooks/useTickets"; import { i18n } from "../../translate/i18n"; import { AuthContext } from "../../context/Auth/AuthContext"; -const useStyles = makeStyles((theme) => ({ - ticketsListWrapper: { - position: "relative", - display: "flex", - height: "100%", - flexDirection: "column", - overflow: "hidden", - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, +const useStyles = makeStyles(theme => ({ + ticketsListWrapper: { + position: "relative", + display: "flex", + height: "100%", + flexDirection: "column", + overflow: "hidden", + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + }, - ticketsList: { - flex: 1, - overflowY: "scroll", - ...theme.scrollbarStyles, - borderTop: "2px solid rgba(0, 0, 0, 0.12)", - }, + ticketsList: { + flex: 1, + overflowY: "scroll", + ...theme.scrollbarStyles, + borderTop: "2px solid rgba(0, 0, 0, 0.12)", + }, - ticketsListHeader: { - color: "rgb(67, 83, 105)", - zIndex: 2, - backgroundColor: "white", - borderBottom: "1px solid rgba(0, 0, 0, 0.12)", - display: "flex", - alignItems: "center", - justifyContent: "space-between", - }, + ticketsListHeader: { + color: "rgb(67, 83, 105)", + zIndex: 2, + backgroundColor: "white", + borderBottom: "1px solid rgba(0, 0, 0, 0.12)", + display: "flex", + alignItems: "center", + justifyContent: "space-between", + }, - ticketsCount: { - fontWeight: "normal", - color: "rgb(104, 121, 146)", - marginLeft: "8px", - fontSize: "14px", - }, + ticketsCount: { + fontWeight: "normal", + color: "rgb(104, 121, 146)", + marginLeft: "8px", + fontSize: "14px", + }, - noTicketsText: { - textAlign: "center", - color: "rgb(104, 121, 146)", - fontSize: "14px", - lineHeight: "1.4", - }, + noTicketsText: { + textAlign: "center", + color: "rgb(104, 121, 146)", + fontSize: "14px", + lineHeight: "1.4", + }, - noTicketsTitle: { - textAlign: "center", - fontSize: "16px", - fontWeight: "600", - margin: "0px", - }, + noTicketsTitle: { + textAlign: "center", + fontSize: "16px", + fontWeight: "600", + margin: "0px", + }, - noTicketsDiv: { - display: "flex", - height: "100px", - margin: 40, - flexDirection: "column", - alignItems: "center", - justifyContent: "center", - }, + noTicketsDiv: { + display: "flex", + height: "100px", + margin: 40, + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + }, })); const reducer = (state, action) => { - if (action.type === "LOAD_TICKETS") { - const newTickets = action.payload; + if (action.type === "LOAD_TICKETS") { + const newTickets = action.payload; + + + newTickets.forEach(ticket => { - newTickets.forEach((ticket) => { - const ticketIndex = state.findIndex((t) => t.id === ticket.id); - if (ticketIndex !== -1) { - state[ticketIndex] = ticket; - if (ticket.unreadMessages > 0) { - state.unshift(state.splice(ticketIndex, 1)[0]); - } - } else { - state.push(ticket); - } - }); + // console.log('* ticket.unreadMessages: ',ticket.unreadMessages) - return [...state]; - } + const ticketIndex = state.findIndex(t => t.id === ticket.id); + if (ticketIndex !== -1) { + state[ticketIndex] = ticket; + if (ticket.unreadMessages > 0) { + state.unshift(state.splice(ticketIndex, 1)[0]); + } + } else { + state.push(ticket); + } + }); - if (action.type === "RESET_UNREAD") { - const ticketId = action.payload; + return [...state]; + } - const ticketIndex = state.findIndex((t) => t.id === ticketId); - if (ticketIndex !== -1) { - state[ticketIndex].unreadMessages = 0; - } + if (action.type === "RESET_UNREAD") { + const ticketId = action.payload; - return [...state]; - } + const ticketIndex = state.findIndex(t => t.id === ticketId); + if (ticketIndex !== -1) { + state[ticketIndex].unreadMessages = 0; + } - if (action.type === "UPDATE_TICKET") { - const ticket = action.payload; + return [...state]; + } - const ticketIndex = state.findIndex((t) => t.id === ticket.id); - if (ticketIndex !== -1) { - state[ticketIndex] = ticket; - } else { - state.unshift(ticket); - } + if (action.type === "UPDATE_TICKET") { + const ticket = action.payload; - return [...state]; - } + // console.log('++++++++++++ UPDATE_TICKET: ',ticket) - if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") { - const message = action.payload.message; + const ticketIndex = state.findIndex(t => t.id === ticket.id); + if (ticketIndex !== -1) { + state[ticketIndex] = ticket; + } else { + state.unshift(ticket); + } - const ticket = action.payload.ticket; + return [...state]; + } - const ticketIndex = state.findIndex((t) => t.id === ticket.id); + if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") { - if (ticketIndex !== -1) { - if (!message.fromMe) { - ticket.unreadMessages += 1; - } + const message = action.payload.message - state[ticketIndex] = ticket; - state.unshift(state.splice(ticketIndex, 1)[0]); - } else { - state.unshift(ticket); - } + const ticket = action.payload.ticket; - return [...state]; - } + const ticketIndex = state.findIndex(t => t.id === ticket.id); - if (action.type === "UPDATE_TICKET_CONTACT") { - const contact = action.payload; - const ticketIndex = state.findIndex((t) => t.contactId === contact.id); - if (ticketIndex !== -1) { - state[ticketIndex].contact = contact; - } - return [...state]; - } + if (ticketIndex !== -1) { + + // console.log('>>>>>> ticketIndex: ', ticketIndex) - if (action.type === "DELETE_TICKET") { - const ticketId = action.payload; - const ticketIndex = state.findIndex((t) => t.id === ticketId); - if (ticketIndex !== -1) { - state.splice(ticketIndex, 1); - } + // console.log('&&&&&&& UPDATE_TICKET_UNREAD_MESSAGES ticket: ',ticket, ' |\n MESSAGE: ', message) + + if(!message.fromMe){ + ticket.unreadMessages +=1 + } - return [...state]; - } + state[ticketIndex] = ticket; + state.unshift(state.splice(ticketIndex, 1)[0]); + + } else { + state.unshift(ticket); + } - if (action.type === "RESET") { - return []; - } + return [...state]; + } + + if (action.type === "UPDATE_TICKET_CONTACT") { + const contact = action.payload; + const ticketIndex = state.findIndex(t => t.contactId === contact.id); + if (ticketIndex !== -1) { + state[ticketIndex].contact = contact; + } + return [...state]; + } + + if (action.type === "DELETE_TICKET") { + const ticketId = action.payload; + const ticketIndex = state.findIndex(t => t.id === ticketId); + if (ticketIndex !== -1) { + state.splice(ticketIndex, 1); + } + + return [...state]; + } + + if (action.type === "RESET") { + return []; + } }; -const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount, style }) => { - const classes = useStyles(); - const [pageNumber, setPageNumber] = useState(1); - const [ticketsList, dispatch] = useReducer(reducer, []); - const { user } = useContext(AuthContext); +const TicketsList = (props) => { + const { status, searchParam, showAll, selectedQueueIds, updateCount, style } = + props; + const classes = useStyles(); + const [pageNumber, setPageNumber] = useState(1); + const [ticketsList, dispatch] = useReducer(reducer, []); + const { user } = useContext(AuthContext); - useEffect(() => { - dispatch({ type: "RESET" }); - setPageNumber(1); - }, [status, searchParam, dispatch, showAll, selectedQueueIds]); + useEffect(() => { + dispatch({ type: "RESET" }); + setPageNumber(1); + }, [status, searchParam, dispatch, showAll, selectedQueueIds]); - const { tickets, hasMore, loading } = useTickets({ - pageNumber, - searchParam, - status, - showAll, - queueIds: JSON.stringify(selectedQueueIds), - }); + const { tickets, hasMore, loading } = useTickets({ + pageNumber, + searchParam, + status, + showAll, + queueIds: JSON.stringify(selectedQueueIds), + }); - useEffect(() => { - if (!status && !searchParam) return; - dispatch({ - type: "LOAD_TICKETS", - payload: tickets, - }); - }, [tickets, status, searchParam]); + useEffect(() => { + if (!status && !searchParam) return; + dispatch({ + type: "LOAD_TICKETS", + payload: tickets, + }); + }, [tickets, status, searchParam]); - useEffect(() => { - const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + useEffect(() => { + const socket = openSocket(process.env.REACT_APP_BACKEND_URL); - const shouldUpdateTicket = (ticket) => - (!ticket.userId || ticket.userId === user?.id || showAll) && - (!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1); + const shouldUpdateTicket = ticket => + (!ticket.userId || ticket.userId === user?.id || showAll) && + (!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1); - const notBelongsToUserQueues = (ticket) => - ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1; + const notBelongsToUserQueues = ticket => + ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1; - socket.on("connect", () => { - if (status) { - socket.emit("joinTickets", status); - } else { - socket.emit("joinNotification"); - } - }); + socket.on("connect", () => { + if (status) { + socket.emit("joinTickets", status); + } else { + socket.emit("joinNotification"); + } - socket.on("ticket", (data) => { - if (data.action === "updateUnread") { - dispatch({ - type: "RESET_UNREAD", - payload: data.ticketId, - }); - } + }); - if (data.action === "update" && shouldUpdateTicket(data.ticket)) { - dispatch({ - type: "UPDATE_TICKET", - payload: data.ticket, - }); - } + socket.on("ticket", data => { + if (data.action === "updateUnread") { + dispatch({ + type: "RESET_UNREAD", + payload: data.ticketId, + }); + } - if (data.action === "update" && notBelongsToUserQueues(data.ticket)) { - dispatch({ type: "DELETE_TICKET", payload: data.ticket.id }); - } + if (data.action === "update" && shouldUpdateTicket(data.ticket)) { + dispatch({ + type: "UPDATE_TICKET", + payload: data.ticket, + }); + } - if (data.action === "delete") { - dispatch({ type: "DELETE_TICKET", payload: data.ticketId }); - } - }); + if (data.action === "update" && notBelongsToUserQueues(data.ticket)) { + dispatch({ type: "DELETE_TICKET", payload: data.ticket.id }); + } - socket.on("appMessage", (data) => { - if (data.action === "create" && shouldUpdateTicket(data.ticket)) { - dispatch({ - type: "UPDATE_TICKET_UNREAD_MESSAGES", - // payload: data.ticket, - payload: data, - }); - } - }); + if (data.action === "delete") { + dispatch({ type: "DELETE_TICKET", payload: data.ticketId }); + } + }); - socket.on("contact", (data) => { - if (data.action === "update") { - dispatch({ - type: "UPDATE_TICKET_CONTACT", - payload: data.contact, - }); - } - }); + socket.on("appMessage", data => { + if (data.action === "create" && shouldUpdateTicket(data.ticket)) { - return () => { - socket.disconnect(); - }; - }, [status, showAll, user, selectedQueueIds]); + // console.log('((((((((((((((((((( DATA.MESSAGE: ', data.message) - useEffect(() => { - if (typeof updateCount === "function") { - updateCount(ticketsList.length); - } - }, [ticketsList]); + dispatch({ + type: "UPDATE_TICKET_UNREAD_MESSAGES", + // payload: data.ticket, + payload: data, + }); + } + }); - const loadMore = () => { - setPageNumber((prevState) => prevState + 1); - }; + socket.on("contact", data => { + if (data.action === "update") { + dispatch({ + type: "UPDATE_TICKET_CONTACT", + payload: data.contact, + }); + } + }); - const handleScroll = (e) => { - if (!hasMore || loading) return; + return () => { + socket.disconnect(); + }; + }, [status, showAll, user, selectedQueueIds]); - const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + useEffect(() => { - if (scrollHeight - (scrollTop + 100) < clientHeight) { - loadMore(); - } - }; - return ( - - - - {ticketsList.length === 0 && !loading ? ( -
- {i18n.t("ticketsList.noTicketsTitle")} -

{i18n.t("ticketsList.noTicketsMessage")}

-
- ) : ( - <> - {ticketsList.map((ticket) => ( - - ))} - - )} - {loading && } -
-
-
- ); + + if (typeof updateCount === "function") { + updateCount(ticketsList.length); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ticketsList]); + + const loadMore = () => { + setPageNumber(prevState => prevState + 1); + }; + + const handleScroll = e => { + if (!hasMore || loading) return; + + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + + if (scrollHeight - (scrollTop + 100) < clientHeight) { + loadMore(); + } + }; + + return ( + + + + {ticketsList.length === 0 && !loading ? ( +
+ + {i18n.t("ticketsList.noTicketsTitle")} + +

+ {i18n.t("ticketsList.noTicketsMessage")} +

+
+ ) : ( + <> + {ticketsList.map(ticket => ( + + ))} + + )} + {loading && } +
+
+
+ ); }; export default TicketsList; - diff --git a/frontend/src/components/TicketsManager/TicketsManager.jsx b/frontend/src/components/TicketsManager/TicketsManager.jsx index b1af5b8..29b74fb 100644 --- a/frontend/src/components/TicketsManager/TicketsManager.jsx +++ b/frontend/src/components/TicketsManager/TicketsManager.jsx @@ -1,337 +1,64 @@ -import React, { useContext, useEffect, useRef, useState } from "react"; -import openSocket from "socket.io-client" - -import { makeStyles } from "@material-ui/core/styles"; -import Paper from "@material-ui/core/Paper"; -import SearchIcon from "@material-ui/icons/Search"; -import InputBase from "@material-ui/core/InputBase"; -import Tabs from "@material-ui/core/Tabs"; -import Tab from "@material-ui/core/Tab"; -import Badge from "@material-ui/core/Badge"; -import MoveToInboxIcon from "@material-ui/icons/MoveToInbox"; -import CheckBoxIcon from "@material-ui/icons/CheckBox"; -import FormControlLabel from "@material-ui/core/FormControlLabel"; -import Switch from "@material-ui/core/Switch"; -import { Button } from "@material-ui/core"; +import React from "react"; import NewTicketModal from "../NewTicketModal"; -import TicketsList from "../TicketsList"; -import TabPanel from "../TabPanel"; +import TicketsList from "../TicketsList/TicketsList"; import { i18n } from "../../translate/i18n"; import { AuthContext } from "../../context/Auth/AuthContext"; import { Can } from "../Can"; import TicketsQueueSelect from "../TicketsQueueSelect"; - -const useStyles = makeStyles((theme) => ({ - ticketsWrapper: { - position: "relative", - display: "flex", - height: "100%", - flexDirection: "column", - overflow: "hidden", - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, - - tabsHeader: { - flex: "none", - backgroundColor: "#eee", - }, - - settingsIcon: { - alignSelf: "center", - marginLeft: "auto", - padding: 8, - }, - - tab: { - minWidth: 120, - width: 120, - }, - - ticketOptionsBox: { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - background: "#fafafa", - padding: theme.spacing(1), - }, - - serachInputWrapper: { - flex: 1, - background: "#fff", - display: "flex", - borderRadius: 40, - padding: 4, - marginRight: theme.spacing(1), - }, - - searchIcon: { - color: "grey", - marginLeft: 6, - marginRight: 6, - alignSelf: "center", - }, - - searchInput: { - flex: 1, - border: "none", - borderRadius: 30, - }, - - badge: { - right: "-10px", - }, - show: { - display: "block", - }, - hide: { - display: "none !important", - }, -})); +import TicketsManagerStyled from "./TicketsManager.style"; const TicketsManager = () => { - const classes = useStyles(); + const [valueTab, setValueTab] = React.useState("open"); - // Old New State - const [newPage, setNewPage] = React.useState(true); - // Old New State + const [newTicketModalOpen, setNewTicketModalOpen] = React.useState(false); + const [showAllTickets, setShowAllTickets] = React.useState(false); + const { user } = React.useContext(AuthContext); - const [searchParam, setSearchParam] = useState(""); - const [tab, setTab] = useState("open"); - const [tabOpen, setTabOpen] = useState("open"); - const [newTicketModalOpen, setNewTicketModalOpen] = useState(false); - const [showAllTickets, setShowAllTickets] = useState(false); - const searchInputRef = useRef(); - const { user } = useContext(AuthContext); - - const [openCount, setOpenCount] = useState(0); - const [pendingCount, setPendingCount] = useState(0); const userQueueIds = user.queues.map((q) => q.id); - const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []); - const socket = openSocket.open(process.env.REACT_APP_BACKEND_URL) + const [selectedQueueIds, setSelectedQueueIds] = React.useState(userQueueIds || []); -console.log(user.queues) -console.log(selectedQueueIds) -socket.on("connect", () => { - if ("open") { - socket.emit("joinTickets", "open"); - } else { - socket.emit("joinNotification"); - } -}); - - -socket.on("connect", ()=>{ - -}) - - - useEffect(() => { + React.useEffect(() => { if (user.profile.toUpperCase() === "ADMIN") { setShowAllTickets(true); } }, [user.profile]); - useEffect(() => { - if (tab === "search") { - searchInputRef.current.focus(); - } - }, [tab]); - let searchTimeout; - const handleSearch = (e) => { - const searchedTerm = e.target.value.toLowerCase(); - - clearTimeout(searchTimeout); - - if (searchedTerm === "") { - setSearchParam(searchedTerm); - setTab("open"); - return; - } - - searchTimeout = setTimeout(() => { - setSearchParam(searchedTerm); - }, 500); - }; - - const handleChangeTab = (e, newValue) => { - setTab(newValue); - }; - - const handleChangeTabOpen = (e, newValue) => { - setTabOpen(newValue); - }; - - const applyPanelStyle = (status) => { - if (tabOpen !== status) { - return { width: 0, height: 0 }; - } - }; - - if (newPage) { - return ( - <> - setNewTicketModalOpen(false)} - /> -
- setSelectedQueueIds(values)} - /> - setOpenCount(val)} - style={applyPanelStyle("open")} - /> - setPendingCount(val)} - style={applyPanelStyle("pending")} - /> -
- - ); + const styleTmp = { + padding:"12px 0 ", + cursor: "pointer", } + + + console.log(valueTab); + return ( - + +
    +
  • setValueTab(e.target.id)}> + Abertos +
  • +
  • setValueTab(e.target.id)}> + Pendentes +
  • +
  • setValueTab(e.target.id)}> + Fechados +
  • +
+ setPendingCount(val)} + /> setNewTicketModalOpen(false)} + onClose={(e) => setNewTicketModalOpen(true)} /> - - - } - label={i18n.t("tickets.tabs.open.title")} - classes={{ root: classes.tab }} - /> - } - label={i18n.t("tickets.tabs.closed.title")} - classes={{ root: classes.tab }} - /> - } - label={i18n.t("tickets.tabs.search.title")} - classes={{ root: classes.tab }} - /> - - - - {tab === "search" ? ( -
- - -
- ) : ( - <> - - ( - setShowAllTickets((prevState) => !prevState)} - name="showAllTickets" - color="primary" - /> - } - /> - )} - /> - - )} - setSelectedQueueIds(values)} - /> -
- - - - {i18n.t("ticketsList.assignedHeader")} - - } - value={"open"} - /> - - {i18n.t("ticketsList.pendingHeader")} - - } - value={"pending"} - /> - - - setOpenCount(val)} - style={applyPanelStyle("open")} - /> - setPendingCount(val)} - style={applyPanelStyle("pending")} - /> - - - - - - - - -
+ ); }; diff --git a/frontend/src/components/TicketsManager/TicketsManager.style.jsx b/frontend/src/components/TicketsManager/TicketsManager.style.jsx new file mode 100644 index 0000000..1ed325f --- /dev/null +++ b/frontend/src/components/TicketsManager/TicketsManager.style.jsx @@ -0,0 +1,7 @@ +import styled from "styled-components" + +const TicketsManagerStyled = styled.div` + display:flex; + flex-direction: column; +` +export default TicketsManagerStyled \ No newline at end of file diff --git a/frontend/src/pages/Tickets/Tickets.style.jsx b/frontend/src/pages/Tickets/Tickets.style.jsx index c8c3ea7..b690fc6 100644 --- a/frontend/src/pages/Tickets/Tickets.style.jsx +++ b/frontend/src/pages/Tickets/Tickets.style.jsx @@ -3,7 +3,8 @@ import styled from "styled-components"; const TicketsStyled = styled.div` display: flex; flex-direction: row; - height: 86vh; + min-height: auto; + `; export default TicketsStyled \ No newline at end of file From 0e39226e96c7621342ee9da79f03748d9ee38be6 Mon Sep 17 00:00:00 2001 From: RenatoDiGiacomo Date: Mon, 25 Jul 2022 15:18:27 -0300 Subject: [PATCH 5/9] Tab Tickets ready --- .../MainContainer/MainContainer.style.jsx | 1 - .../Ticket/TicketsManager/TicketsManager.jsx | 62 ++++++++++++++++++ .../TicketsManager/TicketsManager.style.jsx | 12 ++++ .../TicketsTabs/TicketsTab/TicketsTab.jsx | 27 ++++++++ .../TicketsTab/TicketsTab.style.jsx | 36 ++++++++++ .../TicketsTabs/TicketsTabs.jsx | 18 +++++ .../TicketsTabs/TicketsTabs.style.jsx | 12 ++++ .../{ => Ticket}/TicketsManager/index.js | 0 .../TicketListItem/TicketListItem.jsx | 1 - .../TicketsManager/TicketsManager.jsx | 65 ------------------- .../TicketsManager/TicketsManager.style.jsx | 7 -- frontend/src/pages/Tickets/Tickets.jsx | 2 +- frontend/src/style/varibles.jsx | 19 +++--- 13 files changed, 178 insertions(+), 84 deletions(-) create mode 100644 frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx create mode 100644 frontend/src/components/Ticket/TicketsManager/TicketsManager.style.jsx create mode 100644 frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.jsx create mode 100644 frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.style.jsx create mode 100644 frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.jsx create mode 100644 frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.style.jsx rename frontend/src/components/{ => Ticket}/TicketsManager/index.js (100%) delete mode 100644 frontend/src/components/TicketsManager/TicketsManager.jsx delete mode 100644 frontend/src/components/TicketsManager/TicketsManager.style.jsx diff --git a/frontend/src/components/Base/MainContainer/MainContainer.style.jsx b/frontend/src/components/Base/MainContainer/MainContainer.style.jsx index ac76b22..3ee5bbd 100644 --- a/frontend/src/components/Base/MainContainer/MainContainer.style.jsx +++ b/frontend/src/components/Base/MainContainer/MainContainer.style.jsx @@ -24,7 +24,6 @@ const ContentContainerStyled = styled.div` margin-top: 16px; border-radius: 5px; background-color: ${color.complement.azulOscuro}; - padding: 6px; `; export default MainContainerStyled; diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx new file mode 100644 index 0000000..da928ae --- /dev/null +++ b/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx @@ -0,0 +1,62 @@ +import React from "react"; + +import NewTicketModal from "../../NewTicketModal"; +import TicketsList from "../../TicketsList/TicketsList"; + +import { AuthContext } from "../../../context/Auth/AuthContext"; +import { i18n } from "../../../translate/i18n"; +import { Can } from "../../Can"; +import TicketsQueueSelect from "../../TicketsQueueSelect"; +import TicketsManagerStyled from "./TicketsManager.style"; +import TicketsTabs from "./TicketsTabs/TicketsTabs"; + +const TicketsManager = () => { + const [valueTab, setValueTab] = React.useState("open"); + const [searchParam, setSearchParam] = React.useState(""); + + const [newTicketModalOpen, setNewTicketModalOpen] = React.useState(false); + 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) => { + const searchedTerm = e.target.value.toLowerCase(); + + clearTimeout(searchTimeout); + + if (searchedTerm === "") { + setSearchParam(searchedTerm); + return; + } + + searchTimeout = setTimeout(() => { + setSearchParam(searchedTerm); + }, 200); + }; + + React.useEffect(() => { + if (user.profile.toUpperCase() === "ADMIN") { + setShowAllTickets(true); + } + }, [user.profile]); + + return ( + + +
+ + +
+ + setNewTicketModalOpen(false)} + /> +
+ ); +}; + +export default TicketsManager; diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsManager.style.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsManager.style.jsx new file mode 100644 index 0000000..660129e --- /dev/null +++ b/frontend/src/components/Ticket/TicketsManager/TicketsManager.style.jsx @@ -0,0 +1,12 @@ +import styled from "styled-components"; +import {color} from "../../../style/varibles" + +const TicketsManagerStyled = styled.div` + display:flex; + flex-direction: column; + width: 100%; + height: 100vh; + max-width: 425px; + border-right:1px solid ${color.gradient.border}; +` +export default TicketsManagerStyled \ No newline at end of file diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.jsx new file mode 100644 index 0000000..dde8583 --- /dev/null +++ b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.jsx @@ -0,0 +1,27 @@ +import React from "react"; +import { TicketsTabStyled } from "./TicketsTab.style"; + +const TicketsTab = ({ text, id, setValueTab, valueTab }) => { + const [active, setActive] = React.useState(false); + + const handleClick = ({ target }) => { + setValueTab(target.id); + }; + + React.useEffect(() => { + valueTab === id ? setActive(true) : setActive(false); + }, [valueTab, id]); + return ( + + {text} + + ); +}; + +export default TicketsTab; + diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.style.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.style.jsx new file mode 100644 index 0000000..17c27b4 --- /dev/null +++ b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.style.jsx @@ -0,0 +1,36 @@ +import styled from "styled-components"; +import { color } from "../../../../../style/varibles"; + +export const TicketsTabStyled = styled.li` + width: 100%; + position: relative; + cursor: pointer; + 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}; + } +`; + diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.jsx new file mode 100644 index 0000000..a86715e --- /dev/null +++ b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.jsx @@ -0,0 +1,18 @@ +import React from "react"; +import { TicketTabsStyled } from "./TicketsTabs.style"; + +import TicketsTab from "./TicketsTab/TicketsTab"; + +const TicketsTabs = ({ setValueTab, valueTab }) => { + if (!setValueTab) return null; + return ( + + + + + + ); +}; + +export default TicketsTabs; + diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.style.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.style.jsx new file mode 100644 index 0000000..0ba690e --- /dev/null +++ b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.style.jsx @@ -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; +`; + diff --git a/frontend/src/components/TicketsManager/index.js b/frontend/src/components/Ticket/TicketsManager/index.js similarity index 100% rename from frontend/src/components/TicketsManager/index.js rename to frontend/src/components/Ticket/TicketsManager/index.js diff --git a/frontend/src/components/TicketListItem/TicketListItem.jsx b/frontend/src/components/TicketListItem/TicketListItem.jsx index 9673a1f..7260c7e 100644 --- a/frontend/src/components/TicketListItem/TicketListItem.jsx +++ b/frontend/src/components/TicketListItem/TicketListItem.jsx @@ -21,7 +21,6 @@ const TicketListItem = ({ ticket, status }) => { const handleSelectTicket = (id) => { history.push(`/tickets/${id}`); }; - console.log(ticket.id); if (ticket.status !== status) return null; return ( diff --git a/frontend/src/components/TicketsManager/TicketsManager.jsx b/frontend/src/components/TicketsManager/TicketsManager.jsx deleted file mode 100644 index 29b74fb..0000000 --- a/frontend/src/components/TicketsManager/TicketsManager.jsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from "react"; - -import NewTicketModal from "../NewTicketModal"; -import TicketsList from "../TicketsList/TicketsList"; - -import { i18n } from "../../translate/i18n"; -import { AuthContext } from "../../context/Auth/AuthContext"; -import { Can } from "../Can"; -import TicketsQueueSelect from "../TicketsQueueSelect"; -import TicketsManagerStyled from "./TicketsManager.style"; - -const TicketsManager = () => { - const [valueTab, setValueTab] = React.useState("open"); - - const [newTicketModalOpen, setNewTicketModalOpen] = React.useState(false); - 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 || []); - - React.useEffect(() => { - if (user.profile.toUpperCase() === "ADMIN") { - setShowAllTickets(true); - } - }, [user.profile]); - - - - const styleTmp = { - padding:"12px 0 ", - cursor: "pointer", - } - - - console.log(valueTab); - - return ( - -
    -
  • setValueTab(e.target.id)}> - Abertos -
  • -
  • setValueTab(e.target.id)}> - Pendentes -
  • -
  • setValueTab(e.target.id)}> - Fechados -
  • -
- setPendingCount(val)} - /> - setNewTicketModalOpen(true)} - /> -
- ); -}; - -export default TicketsManager; diff --git a/frontend/src/components/TicketsManager/TicketsManager.style.jsx b/frontend/src/components/TicketsManager/TicketsManager.style.jsx deleted file mode 100644 index 1ed325f..0000000 --- a/frontend/src/components/TicketsManager/TicketsManager.style.jsx +++ /dev/null @@ -1,7 +0,0 @@ -import styled from "styled-components" - -const TicketsManagerStyled = styled.div` - display:flex; - flex-direction: column; -` -export default TicketsManagerStyled \ No newline at end of file diff --git a/frontend/src/pages/Tickets/Tickets.jsx b/frontend/src/pages/Tickets/Tickets.jsx index fa7eb4c..f6fbc1f 100644 --- a/frontend/src/pages/Tickets/Tickets.jsx +++ b/frontend/src/pages/Tickets/Tickets.jsx @@ -3,7 +3,7 @@ import { useParams } from "react-router-dom"; import TicketsStyled from "./Tickets.style"; -import TicketsManager from "../../components/TicketsManager/TicketsManager"; +import TicketsManager from "../../components/Ticket/TicketsManager/TicketsManager"; import Ticket from "../../components/Ticket/"; const Tickets = () => { diff --git a/frontend/src/style/varibles.jsx b/frontend/src/style/varibles.jsx index ecf66e6..ef0e05a 100644 --- a/frontend/src/style/varibles.jsx +++ b/frontend/src/style/varibles.jsx @@ -39,22 +39,23 @@ export const color = { grisOscuro: "#3C3C3B", blanco: "#FFFFFF", }, - complement:{ + complement: { azulCielo: "#55A5DC", azulOscuro: "#212F3C", crisClaro: "#F6F6F6", }, - status:{ + status: { no: "#FF0000", yes: "#00BE1E", - warning: "#FFC700" + warning: "#FFC700", }, - gradient:{ - bgOpacity:"#212F3CD8", - placeholder:"#ffffff83" + gradient: { + bgOpacity: "#212f3cd7", + placeholder: "#ffffff83", + border: "#55A5DC3F", + }, + shadow: { + dark: "2px 2px 4px 2px", }, - shadow:{ - dark:"2px 2px 4px 2px" - } }; From edc249ac87fe0f8d57d5a1c25f02b762e63599fd Mon Sep 17 00:00:00 2001 From: RenatoDiGiacomo Date: Mon, 25 Jul 2022 18:38:21 -0300 Subject: [PATCH 6/9] =?UTF-8?q?Lista=20de=20mensagem=20est=C3=A1=20em=20de?= =?UTF-8?q?senvolvimento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ainda no ajuste do reducer e das correções de execução das tarefas de filtragem --- .../Ticket/TicketsManager/TicketsManager.jsx | 23 ++++--- .../TicketListItem/TicketListItem.jsx | 65 +++++++++++++++---- .../TicketListItem/TicketListItem.style.jsx | 31 +++++++++ .../components/TicketsList/TicketsList.jsx | 42 ++++++------ 4 files changed, 120 insertions(+), 41 deletions(-) create mode 100644 frontend/src/components/TicketListItem/TicketListItem.style.jsx diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx index da928ae..6b16cc0 100644 --- a/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx @@ -1,14 +1,11 @@ import React from "react"; -import NewTicketModal from "../../NewTicketModal"; -import TicketsList from "../../TicketsList/TicketsList"; - -import { AuthContext } from "../../../context/Auth/AuthContext"; -import { i18n } from "../../../translate/i18n"; -import { Can } from "../../Can"; -import TicketsQueueSelect from "../../TicketsQueueSelect"; import TicketsManagerStyled from "./TicketsManager.style"; import TicketsTabs from "./TicketsTabs/TicketsTabs"; +import TicketsList from "../../TicketsList/TicketsList"; +import NewTicketModal from "../../NewTicketModal"; + +import { AuthContext } from "../../../context/Auth/AuthContext"; const TicketsManager = () => { const [valueTab, setValueTab] = React.useState("open"); @@ -46,11 +43,19 @@ const TicketsManager = () => { return ( + {/*Input and add new call*/}
- +
- + {/*Input and add new call*/} + + + setNewTicketModalOpen(false)} diff --git a/frontend/src/components/TicketListItem/TicketListItem.jsx b/frontend/src/components/TicketListItem/TicketListItem.jsx index 7260c7e..5a1a341 100644 --- a/frontend/src/components/TicketListItem/TicketListItem.jsx +++ b/frontend/src/components/TicketListItem/TicketListItem.jsx @@ -1,33 +1,74 @@ -import React from "react"; +import React, { useState, useEffect, useRef, useContext } from "react"; import { useHistory, useParams } from "react-router-dom"; import { parseISO, format, isSameDay } from "date-fns"; +import clsx from "clsx"; import { i18n } from "../../translate/i18n"; import api from "../../services/api"; -import ButtonWithSpinner from "../ButtonWithSpinner"; -import MarkdownWrapper from "../MarkdownWrapper"; -import { Tooltip } from "@material-ui/core"; import { AuthContext } from "../../context/Auth/AuthContext"; import toastError from "../../errors/toastError"; +import { + TicketDateStyled, + TicketImgStyled, + TicketListItemStyled, + TicketTitleStyled, +} from "./TicketListItem.style"; -const TicketListItem = ({ ticket, status }) => { +const TicketListItem = ({ tickets }) => { const history = useHistory(); + const [loading, setLoading] = useState(false); const { ticketId } = useParams(); - const isMounted = React.useRef(true); - const { user } = React.useContext(AuthContext); + const isMounted = useRef(true); + const { user } = useContext(AuthContext); + + useEffect(() => { + return () => { + isMounted.current = false; + }; + }, []); + + const handleAcepptTicket = async (id) => { + setLoading(true); + try { + await api.put(`/tickets/${id}`, { + status: "open", + userId: user?.id, + }); + } catch (err) { + setLoading(false); + toastError(err); + } + if (isMounted.current) { + setLoading(false); + } + history.push(`/tickets/${id}`); + }; const handleSelectTicket = (id) => { history.push(`/tickets/${id}`); }; - if (ticket.status !== status) return null; + if (!tickets) return null; return ( - -

{ticket.id}

- {ticket.id} -

{ticket.lastMessage}

+ + + + +

{tickets.contact.name}

+

{tickets.lastMessage}

+
+ + {isSameDay(parseISO(tickets.updatedAt), new Date()) ? ( + <>{format(parseISO(tickets.updatedAt), "HH:mm")} + ) : ( + <>{format(parseISO(tickets.updatedAt), "dd/MM/yyyy")} + )} +

badge

+
+ +
); }; diff --git a/frontend/src/components/TicketListItem/TicketListItem.style.jsx b/frontend/src/components/TicketListItem/TicketListItem.style.jsx new file mode 100644 index 0000000..ccef80d --- /dev/null +++ b/frontend/src/components/TicketListItem/TicketListItem.style.jsx @@ -0,0 +1,31 @@ +import styled from "styled-components"; +import { color } from "../../style/varibles"; + +export const TicketListItemStyled = styled.li` + background-color: ${color.pricinpal.grisOscuro}; + display: flex; + align-items: center; + padding: 0.5rem 6px; + border-bottom: 1.5px solid ${color.gradient.border}; + &:nth-child(1){ + margin-top: 6px; + } +`; + +export const TicketTitleStyled = styled.div` + display: flex; + flex-direction: column; + flex-grow: 1; +`; +export const TicketDateStyled = styled.div` + display: block; + color: white; +`; + +export const TicketImgStyled = styled.img` + width: 40px; + height: 40px; + object-fit: contain; + border-radius: 50%; +`; + diff --git a/frontend/src/components/TicketsList/TicketsList.jsx b/frontend/src/components/TicketsList/TicketsList.jsx index 31aede6..d3f7c66 100644 --- a/frontend/src/components/TicketsList/TicketsList.jsx +++ b/frontend/src/components/TicketsList/TicketsList.jsx @@ -1,4 +1,5 @@ -import React, { useReducer } from "react"; +import React from "react"; +import { useHistory } from "react-router-dom"; import openSocket from "socket.io-client"; import useTickets from "../../hooks/useTickets"; @@ -13,6 +14,8 @@ const reducer = (state, action) => { const newTickets = action.payload; newTickets.forEach((ticket) => { + // console.log("* ticket.unreadMessages: ", ticket.unreadMessages); + const ticketIndex = state.findIndex((t) => t.id === ticket.id); if (ticketIndex !== -1) { state[ticketIndex] = ticket; @@ -41,6 +44,8 @@ const reducer = (state, action) => { if (action.type === "UPDATE_TICKET") { const ticket = action.payload; + // console.log('++++++++++++ UPDATE_TICKET: ',ticket) + const ticketIndex = state.findIndex((t) => t.id === ticket.id); if (ticketIndex !== -1) { state[ticketIndex] = ticket; @@ -95,12 +100,11 @@ const reducer = (state, action) => { return []; } }; - const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount }) => { + const history = useHistory(); const [pageNumber, setPageNumber] = React.useState(1); + const [ticketsList, dispatch] = React.useReducer(reducer, []); const { user } = React.useContext(AuthContext); - const [ticketsList, dispatch] = useReducer(reducer, []); - const { tickets, hasMore, loading } = useTickets({ pageNumber, searchParam, @@ -166,6 +170,8 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou 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, @@ -188,12 +194,19 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou }; }, [status, showAll, user, selectedQueueIds]); + React.useEffect(() => { + if (typeof updateCount === "function") { + updateCount(ticketsList.length); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ticketsList]); + const loadMore = () => { setPageNumber((prevState) => prevState + 1); }; const handleScroll = (e) => { - if (!hasMore || loading) return; + if (!hasMore || loading) return; const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; @@ -201,22 +214,11 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou loadMore(); } }; - + console.log(ticketsList); return ( -
    - {ticketsList.length === 0 && !loading ? ( -
    - {i18n.t("ticketsList.noTicketsTitle")} -

    {i18n.t("ticketsList.noTicketsMessage")}

    -
    - ) : ( - <> - {ticketsList.map((ticket) => ( - - ))} - - )} - {loading && } +
      + {ticketsList && + ticketsList.map((ticket) => )}
    ); }; From 1230348bb8c06b6e8d196e7caa3b668004db7420 Mon Sep 17 00:00:00 2001 From: RenatoDiGiacomo Date: Tue, 26 Jul 2022 17:51:57 -0300 Subject: [PATCH 7/9] Tickets Manager in progress, List of tickets update Organize and styled styled and input seach in function. Badge msg unread done, dating msg done --- .../src/assets/images/User/clientDefault.png | Bin 0 -> 5712 bytes .../components/Base/Badge/BadgeComponent.jsx | 13 ++ .../Base/Badge/BadgeComponent.style.jsx | 22 ++++ frontend/src/components/MessagesList/index.js | 1 + frontend/src/components/Ticket/Ticket.jsx | 122 ++++++++++++++++++ .../TicketListItem/TicketListItem.jsx | 50 ++++--- .../TicketListItem/TicketListItem.style.jsx | 71 ++++++++++ .../{ => Ticket}/TicketListItem/index.js | 6 +- .../Ticket/TicketSearch/TicketSearch.jsx | 10 ++ .../TicketSearch/TicketSearch.styled.jsx | 1 + .../TicketSearchBtn/TicketSearchBtn.jsx | 10 ++ .../TicketSearchBtn.styled.jsx | 12 ++ .../TicketSearchInput/TicketSearchInput.jsx | 15 +++ .../TicketSearchInput.styled.jsx | 17 +++ .../Ticket/TicketSkeleton/TicketSkeleton.jsx | 9 ++ .../TicketSkeleton/TicketSkeleton.style.jsx | 4 + .../TicketsList/TicketsList.jsx | 53 ++------ .../TicketsManager}/TicketsList/index.js | 12 +- .../Ticket/TicketsManager/TicketsManager.jsx | 14 +- .../TicketsManager/TicketsManager.style.jsx | 1 + .../TicketsTab/TicketsTab.style.jsx | 1 + .../TicketsTabs/TicketsTabs.jsx | 2 +- .../components/Ticket/TicketsManager/index.js | 14 +- .../TicketListItem/TicketListItem.style.jsx | 31 ----- frontend/src/pages/Tickets/Tickets.jsx | 2 +- frontend/src/pages/Tickets/index.js | 2 +- frontend/src/style/varibles.jsx | 1 + 27 files changed, 378 insertions(+), 118 deletions(-) create mode 100644 frontend/src/assets/images/User/clientDefault.png create mode 100644 frontend/src/components/Base/Badge/BadgeComponent.jsx create mode 100644 frontend/src/components/Base/Badge/BadgeComponent.style.jsx create mode 100644 frontend/src/components/Ticket/Ticket.jsx rename frontend/src/components/{ => Ticket}/TicketListItem/TicketListItem.jsx (52%) create mode 100644 frontend/src/components/Ticket/TicketListItem/TicketListItem.style.jsx rename frontend/src/components/{ => Ticket}/TicketListItem/index.js (97%) create mode 100644 frontend/src/components/Ticket/TicketSearch/TicketSearch.jsx create mode 100644 frontend/src/components/Ticket/TicketSearch/TicketSearch.styled.jsx create mode 100644 frontend/src/components/Ticket/TicketSearch/TicketSearchBtn/TicketSearchBtn.jsx create mode 100644 frontend/src/components/Ticket/TicketSearch/TicketSearchBtn/TicketSearchBtn.styled.jsx create mode 100644 frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.jsx create mode 100644 frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.styled.jsx create mode 100644 frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.jsx create mode 100644 frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.style.jsx rename frontend/src/components/{ => Ticket/TicketsManager}/TicketsList/TicketsList.jsx (79%) rename frontend/src/components/{ => Ticket/TicketsManager}/TicketsList/index.js (96%) delete mode 100644 frontend/src/components/TicketListItem/TicketListItem.style.jsx diff --git a/frontend/src/assets/images/User/clientDefault.png b/frontend/src/assets/images/User/clientDefault.png new file mode 100644 index 0000000000000000000000000000000000000000..75dbecebb122ae06ddd64a8c04696f6951bd97b9 GIT binary patch literal 5712 zcmeG=XE5Ax(}dvk97*nUamuCWy+r5loL(YYI4vU4OF|H$-9@<*@AvoFnXSLwo!y9 zun5IM($Qdpe@B0FQ&6wd@7xF0O}?Qn0RgKgsjK?Ax#yE9A>pCF6OUM zTTR5dX;NtUb|Z1Gp*TENBaQ_Ve7-a+xIM~vyD_Y%pn1cNTCXf7wDXOH)U9!s zm{CDkO`~PI@z|F@n`Rv>wZd`YHxEd0#QdXO%E@_)8zWD!I<*Il4(%L;uZO=fknO4*$u{lfPyN%*eXD?0p0 zWo0;3)zek3Q-uZ2T8=_LG^nFwYcb64IT+F7SoZ8>j!Wp_1$OS&7ka-oTv0gS3QR~4 zt|(%039YS%F+`-Qp}ZY2i3mG7^o&B4zvZq1HZEXNFLrl_0&PAV;y)FljTJE1>xM0#u zH}UdUnfM&*qCoy2*zHwho-Y6xqHbhUgSxncMz{bG>v3`1BWv|G@kEdeGdS|E0kX*8 z7`u}yUV-5IZqwIc&DdjIX)R>=L<*>m5pRuA`y`VKlI2Y(XZAvKbI$=nB9S&R)_Kc1c)=PV}wl(KY<(-}S_HqMYAdRq+vru{Ca>?XgH(bt7BKo{ms*M<^Dl ztZAf7)zk4P7$pOviiQW@u%H8#aW@V#gJ*ikojurvfkL@~Xll90IS3_aPd0d27Vu6Q z;Y`wq3`v}Odf}x+?o`4q$c}Pm@<*%|ZYKEiz=|x%z%9VoZWC9A%?lnCXyjMVHcZN* z0c~*wuRI2fuMZ0dV)I0ax}qa%=)q$NUZ*V*EA!8aXimxz~)euPZ02!WbjH7Ht%kpZklGKwLwzJ z3+e-?$}bSC2odbe1}kbLcLxG$3S_;jusj+vLKKE4kP%m4c&d$59jpUpBh?lVS8xw@ zW`PxDlYz^yqJDC>HDDZ`uX{I&`hZ8}*ELv?+y?HVW_yBeP6YKqF1cG7F!tNTL9uy+ zQGwU|>MB6e3>D~+w2H3)tf-aTtp*qqhXm$dQjf>*9LvIrf@R2^o`83FzT8F1nz%VD zI*=zvqXY;%BNDt!19)E_=3Li^Jf&~TIM>;Rr2QLKz zLIVZD15wUPtiekTfR5}a11DelvkZHYXy>&zIsAISq@?imZ(i1?uJ?Rlk%YXBK zzggsfvS3&H6v)E1%>x`xNmWc~YLrKTs;I#+4C%(#HQJ18Q_-KRSiu-H@*NuM&DF1^ z$N^546f%awZj+FCD8S0MH(h>J0XxJl<%8j0?18(rrL3!InD<4&W1`^1EVOA3705z@ ztq=k(%kmWho-xCmNk}XTn7jyt{3r0h5k-#Ba$sgdF~jKZ)JR>au8GoGdL+~~j(s?0 z(@AY*$pXgh!4<{Exxwjr{&O(EKjZPcWm(w>t&K~HdzXNB_HRe+BegyRT;{eM<^Wfm zxhvk9tOeO{A7ujrfs09f`EA#xWlKe{T2j=S*&9{k_jzO zyHfNoiGX$9_>9lDnE@qpQK>o<-G$KUrDX9owhVpQFW>@}{4?xHtmXzksPzcfmT%$< zF+O+rdd3|l8~WNB+Bwv~d*$0{@9+_0$=l2?pDw<5r5_}Poh`i1OYswUDMbkF4JB`W zmcsCzDU=^|r-v6Wek(d2?ZY*)L_ICZ`zpwGadus8GR}>Hr!!W?48FY6r+in|{bb~D zl82-j!+s=R{p7W6-2cM#uLfJi+(*Z&Hx1Qq8@^w7^FcJ+dqEvr#3kuGm3tgeET+pS`f=^s zeT9R!ve@#f=I0ZNVU?%9Su4Jr9{1l(fA$@R#jXT4ZJ`ZjbiIN!-YGWrhko|iOU?G> z``)AAxngDm)jB9Ti9`M~8D~Uo^OkL=G4vQOx;KAsS?E;~xRL!8IP?o*5tTQgi58k${IG+V5|sQ+H>Sso$2V2~?25)bbkU>KFgE^_ z%wL=_Dt!Id59WGz!FEf9s(YfJlBYc$3AR_entODs_9!jCx7I=Zi(1B3EDM#m!Auzj zOIVIob>}K@S56-KD>>)QZX`kS4T5+;#~)&@i@B`Kh2^bubL~`S3Fz}Y&BWRGnq1kc zFW{ML#hW*YpT)o2Pq4l=f3hUo={&vSWb!89uN~&6_0b9wF?%v!oAS(HQC&Ct>6Ah9 zd5T0xF~e4I!8*a+WiaOGjVRvdeA1%~8gkxuS$}@MJk}MFq;2Ql$L8L94h(Kb)e}Zv zyZ6o)=E`#f=&e6U9YH4s+W`iT`>~KKogo#GQq62Iw>?@of z?iJV36OsnbP+hsA@I zqjI5^Ck4o+uTjxNkA=6oXNz3frg@{HgP#1cP+l#Q5;y)iqG6Ay<#{P9phXn15-1(> zq0<;84o-YzrH*c$Qm);6gt$%9s`o6{7*)Y&sQ426n#X$>i9m+2(5`wBJDiOKYS*O^ zj|U<`cnUk#eB_B?f`YQszZWxjO;BSjWzV9(1#oZV+VrTTF*at)DoLKe{&kD0BSoFfm(T5n}1lvH_1GP#<~{@lb}4-*8u+*hUr+*SV;=U<-DsRPi8j(hz0&L;`Aq z+Da)K^^mp1&QQYtPD#RBNIlhrkO`wfY}GNlCWGUy3?|>D{O~#^-^lR^3&~g}UM3%Z za_w;y5&wwAMg>kVj41sLCk#ZF=9(bwKsG_9$b^@rP#;Ct; z9B)UVuLo0>M-~XdR)(RD?LX-y}_%TV?1=$kL|e6hdt1^go)$H(t_T zWq11OxJiYnuqgkWfqE=;yOanyy_9yB2>B3GXvgkRZmwUE#-j9$-7mp`$WOABnsnEZ z2x8s&{1)#)r4(!0$4#lEYtqNfq+}4-J?wtVj&6#i znb~rwOw?Z3;`2g4#2ggcssNxB?OIwY645ILQ{ilVaMJ+V+1GFLs8LNh$gk zywmbpoAF0!l6n1g@*I)ZI()R%;l93*Ggi6($!E zAVqtWx{M|H9c8Y3Sw1KBh`z%d=2-HS9(sJh-n$o+ngX%Nq3H6TQ$zR140sQyyQJV# zxT$H!bCGD$%8B==U0*WOd69o3>2>>K{LR+-$JQR< z0d&vPrh`1MuHG{5-_2zBA;bBTc~$?5)LZCQT-obtle3;`Jv);((zOfG9KGko&lV$T ziu^JdGb*#&cKv8%)VWjV*INThMkiDzg?R&}eVpD@CR*P;l<$5}Rd+(-n2?YWv!A8; z6=Xo8Qk@piIn_lY6Y=*$wV;{hP{=Jp%IE0kscUYtH6>{|#dd-7_xt(h-}yBE{J2x- zcGx7@^uVdR>8AYg`ruxkaG>((@7D}`>Z@F`4r6S*g?NYeNjVUig>o)nje|?hxs8D{ zU7qs+UVNpN2J0=YdXS+zH$&2zJXjO>mNmr%uAd2XOGrOmb5->Co$1$=H7 zXzZ+_$7Ve{H_p1of1i(?biH1oMy9(xGH*PV7XiRqvC4rMyhx zfv5F5?Fr-Ywg;UoUvqLKey*ttSNBHO5*)V;RcIKwpKY%w3^rF4OZKzAyLGSe;BQt> zO(K_Ko-T2MiN6OoP2Tqm9lQe#CXEYtK~>Jm(60#tconbK%2#Pk_Gf2Z9 { + return ( + + {counter} + + ); +}; + +export default BadgeComponent; + diff --git a/frontend/src/components/Base/Badge/BadgeComponent.style.jsx b/frontend/src/components/Base/Badge/BadgeComponent.style.jsx new file mode 100644 index 0000000..471ad11 --- /dev/null +++ b/frontend/src/components/Base/Badge/BadgeComponent.style.jsx @@ -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: 16px; + color: ${color.pricinpal.blanco}; + background-color: ${color.status.yes}; +`; + diff --git a/frontend/src/components/MessagesList/index.js b/frontend/src/components/MessagesList/index.js index 3eedb23..6a4745e 100644 --- a/frontend/src/components/MessagesList/index.js +++ b/frontend/src/components/MessagesList/index.js @@ -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", }, diff --git a/frontend/src/components/Ticket/Ticket.jsx b/frontend/src/components/Ticket/Ticket.jsx new file mode 100644 index 0000000..738e433 --- /dev/null +++ b/frontend/src/components/Ticket/Ticket.jsx @@ -0,0 +1,122 @@ +import React, { useState, useEffect } from "react"; +import { useParams, useHistory } from "react-router-dom"; + +import { toast } from "react-toastify"; +import openSocket from "socket.io-client"; +import clsx from "clsx"; + +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] = useState(false); + const [loading, setLoading] = useState(true); + const [contact, setContact] = useState({}); + const [ticket, setTicket] = useState({}); + + const [statusChatEnd, setStatusChatEnd] = useState({}); + + 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]); + + 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); + }; + + const style ={ + height: "fit-content", + width: "100%", + position: "relative", + } + return ( +
    + +
    + +
    +
    + +
    +
    + + + + + + +
    + ); +}; + +export default Ticket; diff --git a/frontend/src/components/TicketListItem/TicketListItem.jsx b/frontend/src/components/Ticket/TicketListItem/TicketListItem.jsx similarity index 52% rename from frontend/src/components/TicketListItem/TicketListItem.jsx rename to frontend/src/components/Ticket/TicketListItem/TicketListItem.jsx index 5a1a341..1a0a397 100644 --- a/frontend/src/components/TicketListItem/TicketListItem.jsx +++ b/frontend/src/components/Ticket/TicketListItem/TicketListItem.jsx @@ -2,26 +2,27 @@ import React, { useState, useEffect, useRef, useContext } from "react"; import { useHistory, useParams } from "react-router-dom"; import { parseISO, format, isSameDay } from "date-fns"; -import clsx from "clsx"; -import { i18n } from "../../translate/i18n"; +import { i18n } from "../../../translate/i18n"; -import api from "../../services/api"; -import { AuthContext } from "../../context/Auth/AuthContext"; -import toastError from "../../errors/toastError"; +import api from "../../../services/api"; +import { AuthContext } from "../../../context/Auth/AuthContext"; +import toastError from "../../../errors/toastError"; import { TicketDateStyled, TicketImgStyled, TicketListItemStyled, TicketTitleStyled, } from "./TicketListItem.style"; +import Loading from "../../LoadingScreen/Loading"; +import BadgeComponent from "../../Base/Badge/BadgeComponent"; +import DefaultUser from "../../../assets/images/User/clientDefault.png"; const TicketListItem = ({ tickets }) => { const history = useHistory(); - const [loading, setLoading] = useState(false); - const { ticketId } = useParams(); - const isMounted = useRef(true); - const { user } = useContext(AuthContext); + const [loading, setLoading] = React.useState(false); + const isMounted = React.useRef(true); + const { user } = React.useContext(AuthContext); useEffect(() => { return () => { @@ -33,7 +34,7 @@ const TicketListItem = ({ tickets }) => { setLoading(true); try { await api.put(`/tickets/${id}`, { - status: "open", + status: tickets.status, userId: user?.id, }); } catch (err) { @@ -47,17 +48,32 @@ const TicketListItem = ({ tickets }) => { }; const handleSelectTicket = (id) => { - history.push(`/tickets/${id}`); + history.push(`/tickets/${tickets.id}`); }; - - if (!tickets) return null; + console.log(tickets); + if (!tickets) return ; return ( - - - + + + {tickets.contact.profilePicUrl ? ( + + ) : ( + + )} +

    {tickets.contact.name}

    {tickets.lastMessage}

    + {tickets.unreadMessages ? ( + + ) : ( + "" + )}
    {isSameDay(parseISO(tickets.updatedAt), new Date()) ? ( @@ -65,9 +81,7 @@ const TicketListItem = ({ tickets }) => { ) : ( <>{format(parseISO(tickets.updatedAt), "dd/MM/yyyy")} )} -

    badge

    -
    ); diff --git a/frontend/src/components/Ticket/TicketListItem/TicketListItem.style.jsx b/frontend/src/components/Ticket/TicketListItem/TicketListItem.style.jsx new file mode 100644 index 0000000..8162421 --- /dev/null +++ b/frontend/src/components/Ticket/TicketListItem/TicketListItem.style.jsx @@ -0,0 +1,71 @@ +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; +`; + +export const TicketImgStyled = styled.img` + width: 40px; + height: 40px; + object-fit: contain; + border-radius: 50%; + margin: 0 6px; +`; + diff --git a/frontend/src/components/TicketListItem/index.js b/frontend/src/components/Ticket/TicketListItem/index.js similarity index 97% rename from frontend/src/components/TicketListItem/index.js rename to frontend/src/components/Ticket/TicketListItem/index.js index 5cc3008..4774b8c 100644 --- a/frontend/src/components/TicketListItem/index.js +++ b/frontend/src/components/Ticket/TicketListItem/index.js @@ -102,10 +102,10 @@ const useStyles = makeStyles(theme => ({ })); const TicketListItem = ({ ticket }) => { - const classes = ReactDOM.useStyles(); - const history = ReactDOM.useHistory(); + const classes = useStyles(); + const history = useHistory(); const [loading, setLoading] = useState(false); - const { ticketId } = ReactDOM.useParams(); + const { ticketId } = useParams(); const isMounted = useRef(true); const { user } = useContext(AuthContext); diff --git a/frontend/src/components/Ticket/TicketSearch/TicketSearch.jsx b/frontend/src/components/Ticket/TicketSearch/TicketSearch.jsx new file mode 100644 index 0000000..d5e2f72 --- /dev/null +++ b/frontend/src/components/Ticket/TicketSearch/TicketSearch.jsx @@ -0,0 +1,10 @@ +import React from 'react' +import TicketSearchInput from './TicketSearchInput/TicketSearchInput' + +const TicketSearch = ({setNewTicketModalOpen, handleSearch}) => { + return ( + + ) +} + +export default TicketSearch \ No newline at end of file diff --git a/frontend/src/components/Ticket/TicketSearch/TicketSearch.styled.jsx b/frontend/src/components/Ticket/TicketSearch/TicketSearch.styled.jsx new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/frontend/src/components/Ticket/TicketSearch/TicketSearch.styled.jsx @@ -0,0 +1 @@ + diff --git a/frontend/src/components/Ticket/TicketSearch/TicketSearchBtn/TicketSearchBtn.jsx b/frontend/src/components/Ticket/TicketSearch/TicketSearchBtn/TicketSearchBtn.jsx new file mode 100644 index 0000000..166d26e --- /dev/null +++ b/frontend/src/components/Ticket/TicketSearch/TicketSearchBtn/TicketSearchBtn.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import {TicketSearchBtnStyled} from "./TicketSearchBtn.styled" + +const TicketSearchBtn = ({setNewTicketModalOpen}) => { + return ( + setNewTicketModalOpen(true)}>+ + ) +} + +export default TicketSearchBtn \ No newline at end of file diff --git a/frontend/src/components/Ticket/TicketSearch/TicketSearchBtn/TicketSearchBtn.styled.jsx b/frontend/src/components/Ticket/TicketSearch/TicketSearchBtn/TicketSearchBtn.styled.jsx new file mode 100644 index 0000000..15cd837 --- /dev/null +++ b/frontend/src/components/Ticket/TicketSearch/TicketSearchBtn/TicketSearchBtn.styled.jsx @@ -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"; +`; + diff --git a/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.jsx b/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.jsx new file mode 100644 index 0000000..3edf9f6 --- /dev/null +++ b/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.jsx @@ -0,0 +1,15 @@ +import React from "react"; +import TicketSearchBtn from "../TicketSearchBtn/TicketSearchBtn"; +import { TicketSearchDivStyled, TicketSearchInputStyled } from "./TicketSearchInput.styled"; + +const TicketSearchInput = ({ setNewTicketModalOpen, handleSearch }) => { + return ( + + + + + ); +}; + +export default TicketSearchInput; + diff --git a/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.styled.jsx b/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.styled.jsx new file mode 100644 index 0000000..98a7f49 --- /dev/null +++ b/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.styled.jsx @@ -0,0 +1,17 @@ +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 TicketSearchInputStyled = styled.input` + width: 100%; + margin-right: 12px; + background-color: ${color.complement.azulOscuro}; + border: 2px solid ${color.pricinpal.blanco}; + color: ${color.pricinpal.blanco}; + border-radius: 4px; + padding: 4px; +`; diff --git a/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.jsx b/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.jsx new file mode 100644 index 0000000..48ffecd --- /dev/null +++ b/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +const TicketSkeleton = () => { + return ( +
    TicketSkeleton
    + ) +} + +export default TicketSkeleton \ No newline at end of file diff --git a/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.style.jsx b/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.style.jsx new file mode 100644 index 0000000..51fb82d --- /dev/null +++ b/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.style.jsx @@ -0,0 +1,4 @@ +import styled from "styled-components"; +import {color} from "../../../style/varibles" + +export const TicketSkeletonStyled = styled.div``; diff --git a/frontend/src/components/TicketsList/TicketsList.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx similarity index 79% rename from frontend/src/components/TicketsList/TicketsList.jsx rename to frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx index d3f7c66..234f42a 100644 --- a/frontend/src/components/TicketsList/TicketsList.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx @@ -2,20 +2,17 @@ import React from "react"; import { useHistory } from "react-router-dom"; import openSocket from "socket.io-client"; -import useTickets from "../../hooks/useTickets"; -import TicketListItem from "../TicketListItem/TicketListItem"; -import TicketsListSkeleton from "../TicketsListSkeleton"; +import useTickets from "../../../../hooks/useTickets"; +import TicketListItem from "../../TicketListItem/TicketListItem"; +import TicketsListSkeleton from "../../../TicketsListSkeleton"; -import { i18n } from "../../translate/i18n"; -import { AuthContext } from "../../context/Auth/AuthContext"; +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) => { - // console.log("* ticket.unreadMessages: ", ticket.unreadMessages); - const ticketIndex = state.findIndex((t) => t.id === ticket.id); if (ticketIndex !== -1) { state[ticketIndex] = ticket; @@ -26,7 +23,6 @@ const reducer = (state, action) => { state.push(ticket); } }); - return [...state]; } @@ -37,22 +33,17 @@ const reducer = (state, action) => { if (ticketIndex !== -1) { state[ticketIndex].unreadMessages = 0; } - return [...state]; } - if (action.type === "UPDATE_TICKET") { const ticket = action.payload; - // console.log('++++++++++++ UPDATE_TICKET: ',ticket) - const ticketIndex = state.findIndex((t) => t.id === ticket.id); if (ticketIndex !== -1) { state[ticketIndex] = ticket; } else { state.unshift(ticket); } - return [...state]; } @@ -79,6 +70,7 @@ const reducer = (state, action) => { 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; @@ -88,6 +80,7 @@ const reducer = (state, action) => { if (action.type === "DELETE_TICKET") { const ticketId = action.payload; + const ticketIndex = state.findIndex((t) => t.id === ticketId); if (ticketIndex !== -1) { state.splice(ticketIndex, 1); @@ -112,7 +105,6 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou showAll, queueIds: JSON.stringify(selectedQueueIds), }); - React.useEffect(() => { dispatch({ type: "RESET" }); setPageNumber(1); @@ -124,7 +116,7 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou type: "LOAD_TICKETS", payload: tickets, }); - }, [tickets, status, searchParam]); + }, [tickets, searchParam]); React.useEffect(() => { const socket = openSocket(process.env.REACT_APP_BACKEND_URL); @@ -170,8 +162,6 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou 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, @@ -194,31 +184,14 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou }; }, [status, showAll, user, selectedQueueIds]); - React.useEffect(() => { - if (typeof updateCount === "function") { - updateCount(ticketsList.length); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ticketsList]); - const loadMore = () => { - setPageNumber((prevState) => prevState + 1); - }; + if (ticketsList <= 0) return

    carregando

    ; - const handleScroll = (e) => { - if (!hasMore || loading) return; - - const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; - - if (scrollHeight - (scrollTop + 100) < clientHeight) { - loadMore(); - } - }; - console.log(ticketsList); return ( -
      - {ticketsList && - ticketsList.map((ticket) => )} +
        + {ticketsList.map((ticket) => ( + + ))}
      ); }; diff --git a/frontend/src/components/TicketsList/index.js b/frontend/src/components/Ticket/TicketsManager/TicketsList/index.js similarity index 96% rename from frontend/src/components/TicketsList/index.js rename to frontend/src/components/Ticket/TicketsManager/TicketsList/index.js index 1fb3263..c002abc 100644 --- a/frontend/src/components/TicketsList/index.js +++ b/frontend/src/components/Ticket/TicketsManager/TicketsList/index.js @@ -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) diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx index 6b16cc0..cbdf41f 100644 --- a/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx @@ -2,10 +2,11 @@ import React from "react"; import TicketsManagerStyled from "./TicketsManager.style"; import TicketsTabs from "./TicketsTabs/TicketsTabs"; -import TicketsList from "../../TicketsList/TicketsList"; +import TicketsList from "./TicketsList/TicketsList"; import NewTicketModal from "../../NewTicketModal"; import { AuthContext } from "../../../context/Auth/AuthContext"; +import TicketSearch from "../TicketSearch/TicketSearch"; const TicketsManager = () => { const [valueTab, setValueTab] = React.useState("open"); @@ -39,18 +40,13 @@ const TicketsManager = () => { setShowAllTickets(true); } }, [user.profile]); - return ( + - {/*Input and add new call*/} -
      - - -
      - {/*Input and add new call*/} - + { return ( - + ); diff --git a/frontend/src/components/Ticket/TicketsManager/index.js b/frontend/src/components/Ticket/TicketsManager/index.js index 7a2ed26..44c89bc 100644 --- a/frontend/src/components/Ticket/TicketsManager/index.js +++ b/frontend/src/components/Ticket/TicketsManager/index.js @@ -13,14 +13,14 @@ import FormControlLabel from "@material-ui/core/FormControlLabel"; import Switch from "@material-ui/core/Switch"; import { Button } from "@material-ui/core"; -import NewTicketModal from "../NewTicketModal"; -import TicketsList from "../TicketsList"; -import TabPanel from "../TabPanel"; +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"; +import { i18n } from "../../../translate/i18n"; +import { AuthContext } from "../../../context/Auth/AuthContext"; +import { Can } from "../../Can"; +import TicketsQueueSelect from "../../TicketsQueueSelect"; const useStyles = makeStyles((theme) => ({ ticketsWrapper: { diff --git a/frontend/src/components/TicketListItem/TicketListItem.style.jsx b/frontend/src/components/TicketListItem/TicketListItem.style.jsx deleted file mode 100644 index ccef80d..0000000 --- a/frontend/src/components/TicketListItem/TicketListItem.style.jsx +++ /dev/null @@ -1,31 +0,0 @@ -import styled from "styled-components"; -import { color } from "../../style/varibles"; - -export const TicketListItemStyled = styled.li` - background-color: ${color.pricinpal.grisOscuro}; - display: flex; - align-items: center; - padding: 0.5rem 6px; - border-bottom: 1.5px solid ${color.gradient.border}; - &:nth-child(1){ - margin-top: 6px; - } -`; - -export const TicketTitleStyled = styled.div` - display: flex; - flex-direction: column; - flex-grow: 1; -`; -export const TicketDateStyled = styled.div` - display: block; - color: white; -`; - -export const TicketImgStyled = styled.img` - width: 40px; - height: 40px; - object-fit: contain; - border-radius: 50%; -`; - diff --git a/frontend/src/pages/Tickets/Tickets.jsx b/frontend/src/pages/Tickets/Tickets.jsx index f6fbc1f..78ef468 100644 --- a/frontend/src/pages/Tickets/Tickets.jsx +++ b/frontend/src/pages/Tickets/Tickets.jsx @@ -4,7 +4,7 @@ import { useParams } from "react-router-dom"; import TicketsStyled from "./Tickets.style"; import TicketsManager from "../../components/Ticket/TicketsManager/TicketsManager"; -import Ticket from "../../components/Ticket/"; +import Ticket from "../../components/Ticket/Ticket"; const Tickets = () => { const { ticketId } = useParams(); diff --git a/frontend/src/pages/Tickets/index.js b/frontend/src/pages/Tickets/index.js index 461145a..1c68971 100644 --- a/frontend/src/pages/Tickets/index.js +++ b/frontend/src/pages/Tickets/index.js @@ -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"; diff --git a/frontend/src/style/varibles.jsx b/frontend/src/style/varibles.jsx index ef0e05a..6fc6387 100644 --- a/frontend/src/style/varibles.jsx +++ b/frontend/src/style/varibles.jsx @@ -53,6 +53,7 @@ export const color = { bgOpacity: "#212f3cd7", placeholder: "#ffffff83", border: "#55A5DC3F", + text: "#AEBAC1", }, shadow: { dark: "2px 2px 4px 2px", From 3c43d78cfce0e2281dd190f24ae0175a0afdb6ee Mon Sep 17 00:00:00 2001 From: RenatoDiGiacomo Date: Wed, 27 Jul 2022 15:37:23 -0300 Subject: [PATCH 8/9] Ticket in work, Some counter and buttons in pending tickets added skeleton loading ticket created, fix the status tickets create and fix input search --- .vscode/launch.json | 15 ++++ .../src/assets/icons/SearchInput/search.svg | 3 + frontend/src/components/Base/BTN/Btn.jsx | 12 --- frontend/src/components/Base/Btn/Btn.jsx | 13 +++ .../Base/{BTN => Btn}/Btn.styled.jsx | 2 +- .../ConfirmationModal/ConfirmationModal.jsx | 2 +- .../src/components/LoadingScreen/Loading.jsx | 9 --- .../LoadingScreen/LoadingScreen.jsx | 8 ++ ...ding.style.jsx => LoadingScreen.style.jsx} | 0 frontend/src/components/Ticket/Ticket.jsx | 17 ++-- .../Ticket/TicketSearch/TicketSearch.jsx | 4 +- .../TicketSearchInput/TicketSearchInput.jsx | 18 ++++- .../TicketSearchInput.styled.jsx | 45 ++++++++++- .../Ticket/TicketSkeleton/TicketSkeleton.jsx | 9 --- .../TicketSkeleton/TicketSkeleton.style.jsx | 4 - .../TicketListItem/TicketListItem.jsx | 42 ++++++---- .../TicketListItem/TicketListItem.style.jsx | 3 +- .../TicketsList}/TicketListItem/index.js | 0 .../TicketsList/TicketsList.jsx | 18 ++--- .../TicketsList/TicketsList.style.jsx | 10 +++ .../TicketListSkeleton.jsx | 42 ++++++++++ .../TicketListSkeleton.style.jsx | 80 +++++++++++++++++++ .../TicketsListSkeleton/index.js | 0 .../Ticket/TicketsManager/TicketsManager.jsx | 28 ++++--- .../TicketsTabs/TicketsTabs.jsx | 3 +- .../src/components/UserModal/UserModal.jsx | 2 +- frontend/src/layout/index.js | 4 +- frontend/src/routes/Route.js | 8 +- 28 files changed, 302 insertions(+), 99 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 frontend/src/assets/icons/SearchInput/search.svg delete mode 100644 frontend/src/components/Base/BTN/Btn.jsx create mode 100644 frontend/src/components/Base/Btn/Btn.jsx rename frontend/src/components/Base/{BTN => Btn}/Btn.styled.jsx (90%) delete mode 100644 frontend/src/components/LoadingScreen/Loading.jsx create mode 100644 frontend/src/components/LoadingScreen/LoadingScreen.jsx rename frontend/src/components/LoadingScreen/{Loading.style.jsx => LoadingScreen.style.jsx} (100%) delete mode 100644 frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.jsx delete mode 100644 frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.style.jsx rename frontend/src/components/Ticket/{ => TicketsManager/TicketsList}/TicketListItem/TicketListItem.jsx (64%) rename frontend/src/components/Ticket/{ => TicketsManager/TicketsList}/TicketListItem/TicketListItem.style.jsx (95%) rename frontend/src/components/Ticket/{ => TicketsManager/TicketsList}/TicketListItem/index.js (100%) create mode 100644 frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.style.jsx create mode 100644 frontend/src/components/Ticket/TicketsManager/TicketsListSkeleton/TicketListSkeleton.jsx create mode 100644 frontend/src/components/Ticket/TicketsManager/TicketsListSkeleton/TicketListSkeleton.style.jsx rename frontend/src/components/{ => Ticket/TicketsManager}/TicketsListSkeleton/index.js (100%) diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..562cc64 --- /dev/null +++ b/.vscode/launch.json @@ -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}" + } + ] +} \ No newline at end of file diff --git a/frontend/src/assets/icons/SearchInput/search.svg b/frontend/src/assets/icons/SearchInput/search.svg new file mode 100644 index 0000000..6636c58 --- /dev/null +++ b/frontend/src/assets/icons/SearchInput/search.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/src/components/Base/BTN/Btn.jsx b/frontend/src/components/Base/BTN/Btn.jsx deleted file mode 100644 index 9755fa4..0000000 --- a/frontend/src/components/Base/BTN/Btn.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; -import BtnBaseStyled from "./Btn.styled"; - -const BtnComponent = ({ text, bgcolor, fontcolor,...props }) => { - return ( - - {text} - - ); -}; - -export default BtnComponent; diff --git a/frontend/src/components/Base/Btn/Btn.jsx b/frontend/src/components/Base/Btn/Btn.jsx new file mode 100644 index 0000000..4f81980 --- /dev/null +++ b/frontend/src/components/Base/Btn/Btn.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import BtnBaseStyled from "./Btn.styled"; + +const BtnComponent = ({ text, bgcolor, fontSize, fontcolor, ...props }) => { + return ( + + {text} + + ); +}; + +export default BtnComponent; + diff --git a/frontend/src/components/Base/BTN/Btn.styled.jsx b/frontend/src/components/Base/Btn/Btn.styled.jsx similarity index 90% rename from frontend/src/components/Base/BTN/Btn.styled.jsx rename to frontend/src/components/Base/Btn/Btn.styled.jsx index 9773520..e5beb3d 100644 --- a/frontend/src/components/Base/BTN/Btn.styled.jsx +++ b/frontend/src/components/Base/Btn/Btn.styled.jsx @@ -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; diff --git a/frontend/src/components/ConfirmationModal/ConfirmationModal.jsx b/frontend/src/components/ConfirmationModal/ConfirmationModal.jsx index 0f8d424..7db3110 100644 --- a/frontend/src/components/ConfirmationModal/ConfirmationModal.jsx +++ b/frontend/src/components/ConfirmationModal/ConfirmationModal.jsx @@ -1,5 +1,5 @@ import React from "react"; -import BtnComponent from "../Base/BTN/Btn"; +import BtnComponent from "../Base/Btn/Btn"; import { ConfirmationModalStyled, diff --git a/frontend/src/components/LoadingScreen/Loading.jsx b/frontend/src/components/LoadingScreen/Loading.jsx deleted file mode 100644 index ca2e960..0000000 --- a/frontend/src/components/LoadingScreen/Loading.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; - -import LoadingStyled from "./Loading.style" - -const Loading = () => { - return ; -}; - -export default Loading; diff --git a/frontend/src/components/LoadingScreen/LoadingScreen.jsx b/frontend/src/components/LoadingScreen/LoadingScreen.jsx new file mode 100644 index 0000000..3b72d76 --- /dev/null +++ b/frontend/src/components/LoadingScreen/LoadingScreen.jsx @@ -0,0 +1,8 @@ +import React from "react"; +import LoadingStyled from "./LoadingScreen.style" + +const LoadingScreen = () => { + return ; +}; + +export default LoadingScreen; diff --git a/frontend/src/components/LoadingScreen/Loading.style.jsx b/frontend/src/components/LoadingScreen/LoadingScreen.style.jsx similarity index 100% rename from frontend/src/components/LoadingScreen/Loading.style.jsx rename to frontend/src/components/LoadingScreen/LoadingScreen.style.jsx diff --git a/frontend/src/components/Ticket/Ticket.jsx b/frontend/src/components/Ticket/Ticket.jsx index 738e433..1f3d74c 100644 --- a/frontend/src/components/Ticket/Ticket.jsx +++ b/frontend/src/components/Ticket/Ticket.jsx @@ -1,9 +1,8 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { useParams, useHistory } from "react-router-dom"; import { toast } from "react-toastify"; import openSocket from "socket.io-client"; -import clsx from "clsx"; import ContactDrawer from "../ContactDrawer"; import MessageInput from "../MessageInput"; @@ -19,14 +18,14 @@ const Ticket = () => { const { ticketId } = useParams(); const history = useHistory(); - const [drawerOpen, setDrawerOpen] = useState(false); - const [loading, setLoading] = useState(true); - const [contact, setContact] = useState({}); - const [ticket, setTicket] = useState({}); + 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] = useState({}); + const [statusChatEnd, setStatusChatEnd] = React.useState({}); - useEffect(() => { + React.useEffect(() => { setLoading(true); const delayDebounceFn = setTimeout(() => { const fetchTicket = async () => { @@ -49,7 +48,7 @@ const Ticket = () => { return () => clearTimeout(delayDebounceFn); }, [ticketId, history]); - useEffect(() => { + React.useEffect(() => { const socket = openSocket(process.env.REACT_APP_BACKEND_URL); socket.on("connect", () => socket.emit("joinChatBox", ticketId)); diff --git a/frontend/src/components/Ticket/TicketSearch/TicketSearch.jsx b/frontend/src/components/Ticket/TicketSearch/TicketSearch.jsx index d5e2f72..265672f 100644 --- a/frontend/src/components/Ticket/TicketSearch/TicketSearch.jsx +++ b/frontend/src/components/Ticket/TicketSearch/TicketSearch.jsx @@ -1,9 +1,9 @@ import React from 'react' import TicketSearchInput from './TicketSearchInput/TicketSearchInput' -const TicketSearch = ({setNewTicketModalOpen, handleSearch}) => { +const TicketSearch = ({spinning,setNewTicketModalOpen, handleSearch}) => { return ( - + ) } diff --git a/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.jsx b/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.jsx index 3edf9f6..f2127ef 100644 --- a/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.jsx +++ b/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.jsx @@ -1,11 +1,21 @@ import React from "react"; -import TicketSearchBtn from "../TicketSearchBtn/TicketSearchBtn"; -import { TicketSearchDivStyled, TicketSearchInputStyled } from "./TicketSearchInput.styled"; -const TicketSearchInput = ({ setNewTicketModalOpen, handleSearch }) => { +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 ( - + + + + ); diff --git a/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.styled.jsx b/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.styled.jsx index 98a7f49..1ef953d 100644 --- a/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.styled.jsx +++ b/frontend/src/components/Ticket/TicketSearch/TicketSearchInput/TicketSearchInput.styled.jsx @@ -6,12 +6,53 @@ export const TicketSearchDivStyled = styled.div` display: flex; flex-direction: row; `; -export const TicketSearchInputStyled = styled.input` +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: 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; + } +`; + diff --git a/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.jsx b/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.jsx deleted file mode 100644 index 48ffecd..0000000 --- a/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' - -const TicketSkeleton = () => { - return ( -
      TicketSkeleton
      - ) -} - -export default TicketSkeleton \ No newline at end of file diff --git a/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.style.jsx b/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.style.jsx deleted file mode 100644 index 51fb82d..0000000 --- a/frontend/src/components/Ticket/TicketSkeleton/TicketSkeleton.style.jsx +++ /dev/null @@ -1,4 +0,0 @@ -import styled from "styled-components"; -import {color} from "../../../style/varibles" - -export const TicketSkeletonStyled = styled.div``; diff --git a/frontend/src/components/Ticket/TicketListItem/TicketListItem.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/TicketListItem.jsx similarity index 64% rename from frontend/src/components/Ticket/TicketListItem/TicketListItem.jsx rename to frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/TicketListItem.jsx index 1a0a397..ce86ef4 100644 --- a/frontend/src/components/Ticket/TicketListItem/TicketListItem.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/TicketListItem.jsx @@ -1,22 +1,23 @@ -import React, { useState, useEffect, useRef, useContext } from "react"; - -import { useHistory, useParams } from "react-router-dom"; +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 { i18n } from "../../../../../translate/i18n"; -import api from "../../../services/api"; -import { AuthContext } from "../../../context/Auth/AuthContext"; -import toastError from "../../../errors/toastError"; +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 Loading from "../../LoadingScreen/Loading"; -import BadgeComponent from "../../Base/Badge/BadgeComponent"; -import DefaultUser from "../../../assets/images/User/clientDefault.png"; +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(); @@ -24,37 +25,39 @@ const TicketListItem = ({ tickets }) => { const isMounted = React.useRef(true); const { user } = React.useContext(AuthContext); - useEffect(() => { + React.useEffect(() => { return () => { isMounted.current = false; }; }, []); const handleAcepptTicket = async (id) => { - setLoading(true); + setLoading(true) try { await api.put(`/tickets/${id}`, { - status: tickets.status, + 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/${tickets.id}`); + history.push(`/tickets/${id}`); }; - console.log(tickets); - if (!tickets) return ; + if (loading) return ; return ( - + handleSelectTicket(tickets.id)}> {tickets.contact.profilePicUrl ? ( ) : ( @@ -82,6 +85,11 @@ const TicketListItem = ({ tickets }) => { <>{format(parseISO(tickets.updatedAt), "dd/MM/yyyy")} )} + {tickets.status === "pending" ? ( + + ) : ( + "" + )} ); diff --git a/frontend/src/components/Ticket/TicketListItem/TicketListItem.style.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/TicketListItem.style.jsx similarity index 95% rename from frontend/src/components/Ticket/TicketListItem/TicketListItem.style.jsx rename to frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/TicketListItem.style.jsx index 8162421..33b14e8 100644 --- a/frontend/src/components/Ticket/TicketListItem/TicketListItem.style.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/TicketListItem.style.jsx @@ -1,5 +1,5 @@ import styled from "styled-components"; -import { color } from "../../../style/varibles"; +import { color } from "../../../../../style/varibles"; export const TicketListItemStyled = styled.li` cursor: pointer; @@ -59,6 +59,7 @@ export const TicketDateStyled = styled.div` font-family: "Helvetica55"; display: block; color: white; + margin-right: 1rem; `; export const TicketImgStyled = styled.img` diff --git a/frontend/src/components/Ticket/TicketListItem/index.js b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/index.js similarity index 100% rename from frontend/src/components/Ticket/TicketListItem/index.js rename to frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/index.js diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx index 234f42a..d1bf84e 100644 --- a/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx @@ -1,10 +1,11 @@ import React from "react"; -import { useHistory } from "react-router-dom"; + import openSocket from "socket.io-client"; +import TicketListStyled from "./TicketsList.style"; import useTickets from "../../../../hooks/useTickets"; -import TicketListItem from "../../TicketListItem/TicketListItem"; -import TicketsListSkeleton from "../../../TicketsListSkeleton"; +import TicketListItem from "../TicketsList/TicketListItem/TicketListItem"; +import TicketsListSkeleton from "../TicketsListSkeleton/TicketListSkeleton"; import { i18n } from "../../../../translate/i18n"; import { AuthContext } from "../../../../context/Auth/AuthContext"; @@ -94,11 +95,10 @@ const reducer = (state, action) => { } }; const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount }) => { - const history = useHistory(); const [pageNumber, setPageNumber] = React.useState(1); const [ticketsList, dispatch] = React.useReducer(reducer, []); const { user } = React.useContext(AuthContext); - const { tickets, hasMore, loading } = useTickets({ + const { tickets, loading } = useTickets({ pageNumber, searchParam, status, @@ -184,15 +184,13 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou }; }, [status, showAll, user, selectedQueueIds]); - - if (ticketsList <= 0) return

      carregando

      ; - + if (loading) return ; return ( -
        + {ticketsList.map((ticket) => ( ))} -
      + ); }; diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.style.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.style.jsx new file mode 100644 index 0000000..141ae52 --- /dev/null +++ b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.style.jsx @@ -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; diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsListSkeleton/TicketListSkeleton.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsListSkeleton/TicketListSkeleton.jsx new file mode 100644 index 0000000..43ac9f6 --- /dev/null +++ b/frontend/src/components/Ticket/TicketsManager/TicketsListSkeleton/TicketListSkeleton.jsx @@ -0,0 +1,42 @@ +import React from "react"; +import { + TicketListSkeletonStyled, + TicketSkeletonItemStyled, + TicketSkeletonTitleStyled, + TicketSkeletonDateStyled, + TicketSkeletonImgStyled, +} from "./TicketListSkeleton.style"; + +const TicketsSkeleton = () => { + return ( + + + + +
      +
      +
      + +
      + + + +
      +
      +
      + +
      + + + +
      +
      +
      + +
      +
      + ); +}; + +export default TicketsSkeleton; + diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsListSkeleton/TicketListSkeleton.style.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsListSkeleton/TicketListSkeleton.style.jsx new file mode 100644 index 0000000..5dca19e --- /dev/null +++ b/frontend/src/components/Ticket/TicketsManager/TicketsListSkeleton/TicketListSkeleton.style.jsx @@ -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; +`; + diff --git a/frontend/src/components/TicketsListSkeleton/index.js b/frontend/src/components/Ticket/TicketsManager/TicketsListSkeleton/index.js similarity index 100% rename from frontend/src/components/TicketsListSkeleton/index.js rename to frontend/src/components/Ticket/TicketsManager/TicketsListSkeleton/index.js diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx index cbdf41f..ff74461 100644 --- a/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx @@ -4,35 +4,39 @@ import TicketsManagerStyled from "./TicketsManager.style"; import TicketsTabs from "./TicketsTabs/TicketsTabs"; import TicketsList from "./TicketsList/TicketsList"; import NewTicketModal from "../../NewTicketModal"; - +import useTickets from "../../../hooks/useTickets"; import { AuthContext } from "../../../context/Auth/AuthContext"; import TicketSearch from "../TicketSearch/TicketSearch"; 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 [showAllTickets, setShowAllTickets] = React.useState(false); const { user } = React.useContext(AuthContext); - const userQueueIds = user.queues.map((q) => q.id); const [selectedQueueIds, setSelectedQueueIds] = React.useState(userQueueIds || []); + const { tickets } = useTickets({ + searchParam + }); 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; } - - searchTimeout = setTimeout(() => { - setSearchParam(searchedTerm); - }, 200); }; React.useEffect(() => { @@ -40,11 +44,15 @@ const TicketsManager = () => { setShowAllTickets(true); } }, [user.profile]); + return ( - - - + + { +const TicketsTabs = ({ tickets, setValueTab, valueTab }) => { + if (!setValueTab) return null; return ( diff --git a/frontend/src/components/UserModal/UserModal.jsx b/frontend/src/components/UserModal/UserModal.jsx index efa0c0f..f3cd056 100644 --- a/frontend/src/components/UserModal/UserModal.jsx +++ b/frontend/src/components/UserModal/UserModal.jsx @@ -6,7 +6,7 @@ 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"; diff --git a/frontend/src/layout/index.js b/frontend/src/layout/index.js index 2dd0ea9..cfc1d04 100644 --- a/frontend/src/layout/index.js +++ b/frontend/src/layout/index.js @@ -1,6 +1,6 @@ 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"; @@ -10,7 +10,7 @@ const LoggedInLayout = ({ children }) => { const { loading, user } = React.useContext(AuthContext); if (loading) { - return ; + return ; } return ( diff --git a/frontend/src/routes/Route.js b/frontend/src/routes/Route.js index 788501e..8aa58b5 100644 --- a/frontend/src/routes/Route.js +++ b/frontend/src/routes/Route.js @@ -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 && } ); @@ -19,7 +19,7 @@ const Route = ({ component: Component, isPrivate = false, ...rest }) => { if (isAuth && !isPrivate) { return ( <> - {loading && } + {loading && } ; ); @@ -27,7 +27,7 @@ const Route = ({ component: Component, isPrivate = false, ...rest }) => { return ( <> - {loading && } + {loading && } ); From 4c24f81336e9451a05373fb0bb224102bfd203d3 Mon Sep 17 00:00:00 2001 From: RenatoDiGiacomo Date: Thu, 28 Jul 2022 12:15:28 -0300 Subject: [PATCH 9/9] Tabs and Manager list fix some functions WIP Project stop by order chef --- .../components/Base/Badge/BadgeComponent.jsx | 4 +- .../Base/Badge/BadgeComponent.style.jsx | 4 +- .../LoginForm/Inputs/InputComponent.style.jsx | 2 +- frontend/src/components/Ticket/Ticket.jsx | 8 +- .../src/components/Ticket/Ticket.style.jsx | 7 + .../TicketListItem/TicketListItem.jsx | 25 +- .../TicketsList/TicketsList.jsx | 38 +- .../Ticket/TicketsManager/TicketsManager.jsx | 39 +- .../TicketsTabs/TicketsTab/TicketsTab.jsx | 18 +- .../TicketsTabs/TicketsTabs.jsx | 30 +- frontend/src/pages/Report/index.js | 724 +++++++----------- frontend/src/style/varibles.jsx | 2 +- 12 files changed, 425 insertions(+), 476 deletions(-) create mode 100644 frontend/src/components/Ticket/Ticket.style.jsx diff --git a/frontend/src/components/Base/Badge/BadgeComponent.jsx b/frontend/src/components/Base/Badge/BadgeComponent.jsx index 93e1b35..6fe85e5 100644 --- a/frontend/src/components/Base/Badge/BadgeComponent.jsx +++ b/frontend/src/components/Base/Badge/BadgeComponent.jsx @@ -1,9 +1,9 @@ import React from "react"; import { BadgeComponentStyled } from "./BadgeComponent.style"; -const BadgeComponent = ({ counter, position, top, left, right, bottom }) => { +const BadgeComponent = ({ counter,fontSize, position, top, left, right, bottom,bgcolor }) => { return ( - + {counter} ); diff --git a/frontend/src/components/Base/Badge/BadgeComponent.style.jsx b/frontend/src/components/Base/Badge/BadgeComponent.style.jsx index 471ad11..87c5cc8 100644 --- a/frontend/src/components/Base/Badge/BadgeComponent.style.jsx +++ b/frontend/src/components/Base/Badge/BadgeComponent.style.jsx @@ -15,8 +15,8 @@ export const BadgeComponentStyled = styled.span` object-fit: cover; width: 21px; height: 21px; - font-size: 16px; + font-size: ${({ fontSize }) => (fontSize ? fontSize : "16px")}; color: ${color.pricinpal.blanco}; - background-color: ${color.status.yes}; + background-color: ${({ bgcolor }) => (bgcolor ? bgcolor : color.status.yes)}; `; diff --git a/frontend/src/components/LoginComponents/LoginForm/Inputs/InputComponent.style.jsx b/frontend/src/components/LoginComponents/LoginForm/Inputs/InputComponent.style.jsx index 4e6c60d..613c3fc 100644 --- a/frontend/src/components/LoginComponents/LoginForm/Inputs/InputComponent.style.jsx +++ b/frontend/src/components/LoginComponents/LoginForm/Inputs/InputComponent.style.jsx @@ -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}; `; diff --git a/frontend/src/components/Ticket/Ticket.jsx b/frontend/src/components/Ticket/Ticket.jsx index 1f3d74c..929f648 100644 --- a/frontend/src/components/Ticket/Ticket.jsx +++ b/frontend/src/components/Ticket/Ticket.jsx @@ -88,13 +88,9 @@ const Ticket = () => { setDrawerOpen(false); }; - const style ={ - height: "fit-content", - width: "100%", - position: "relative", - } + return ( -
      +
      diff --git a/frontend/src/components/Ticket/Ticket.style.jsx b/frontend/src/components/Ticket/Ticket.style.jsx new file mode 100644 index 0000000..c6e12c9 --- /dev/null +++ b/frontend/src/components/Ticket/Ticket.style.jsx @@ -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}; +`; diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/TicketListItem.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/TicketListItem.jsx index ce86ef4..272817b 100644 --- a/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/TicketListItem.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketListItem/TicketListItem.jsx @@ -1,5 +1,5 @@ import React from "react"; -import {color} from "../../../../../style/varibles"; +import { color } from "../../../../../style/varibles"; import { useHistory } from "react-router-dom"; import { parseISO, format, isSameDay } from "date-fns"; @@ -8,7 +8,7 @@ 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 Btn from "../../../../Base/Btn/Btn"; import { TicketDateStyled, TicketImgStyled, @@ -24,7 +24,6 @@ const TicketListItem = ({ tickets }) => { const [loading, setLoading] = React.useState(false); const isMounted = React.useRef(true); const { user } = React.useContext(AuthContext); - React.useEffect(() => { return () => { isMounted.current = false; @@ -32,7 +31,7 @@ const TicketListItem = ({ tickets }) => { }, []); const handleAcepptTicket = async (id) => { - setLoading(true) + setLoading(true); try { await api.put(`/tickets/${id}`, { status: "open", @@ -54,10 +53,14 @@ const TicketListItem = ({ tickets }) => { const handleSelectTicket = (id) => { history.push(`/tickets/${id}`); }; - if (loading) return ; + + return ( - - handleSelectTicket(tickets.id)}> + + handleSelectTicket(tickets.id)} + > {tickets.contact.profilePicUrl ? ( ) : ( @@ -86,7 +89,13 @@ const TicketListItem = ({ tickets }) => { )} {tickets.status === "pending" ? ( - + handleAcepptTicket(tickets.id)} + text="Aceitar" + bgcolor={color.complement.azulCielo} + fontcolor={color.pricinpal.blanco} + fontSize="12px" + /> ) : ( "" )} diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx index d1bf84e..fb3b27b 100644 --- a/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsList/TicketsList.jsx @@ -2,6 +2,8 @@ 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"; @@ -94,10 +96,14 @@ const reducer = (state, action) => { return []; } }; -const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount }) => { +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, @@ -105,10 +111,13 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou showAll, queueIds: JSON.stringify(selectedQueueIds), }); + React.useEffect(() => { - dispatch({ type: "RESET" }); - setPageNumber(1); - }, [status, searchParam, dispatch, showAll, selectedQueueIds]); + if (typeof updateCount === "function") { + updateCount(ticketsList.length); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ticketsList]); React.useEffect(() => { if (!status && !searchParam) return; @@ -116,7 +125,7 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou type: "LOAD_TICKETS", payload: tickets, }); - }, [tickets, searchParam]); + }, [tickets, status, searchParam]); React.useEffect(() => { const socket = openSocket(process.env.REACT_APP_BACKEND_URL); @@ -162,6 +171,8 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou 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, @@ -185,13 +196,16 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou }, [status, showAll, user, selectedQueueIds]); if (loading) return ; - return ( - - {ticketsList.map((ticket) => ( - - ))} - - ); + + if (status === valueTab) + return ( + + {ticketsList.map((ticket) => ( + + ))} + + ); + return null; }; export default TicketsList; diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx index ff74461..d971cf4 100644 --- a/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsManager.jsx @@ -2,24 +2,25 @@ 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 useTickets from "../../../hooks/useTickets"; + import { AuthContext } from "../../../context/Auth/AuthContext"; -import TicketSearch from "../TicketSearch/TicketSearch"; 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 || []); - const { tickets } = useTickets({ - searchParam - }); let searchTimeout; const handleSearch = (e) => { @@ -47,23 +48,47 @@ const TicketsManager = () => { return ( - + + + setOpenCount(v)} showAll={showAllTickets} - status={valueTab} selectedQueueIds={selectedQueueIds} searchParam={searchParam} + valueTab={valueTab} /> + setPendingCount(v)} + selectedQueueIds={selectedQueueIds} + searchParam={searchParam} + valueTab={valueTab} + /> + setClosedCount(v)} + selectedQueueIds={selectedQueueIds} + searchParam={searchParam} + valueTab={valueTab} + /> + setNewTicketModalOpen(false)} /> + ); }; diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.jsx index dde8583..de62601 100644 --- a/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTab/TicketsTab.jsx @@ -1,7 +1,10 @@ import React from "react"; import { TicketsTabStyled } from "./TicketsTab.style"; +import { color } from "../../../../../style/varibles"; -const TicketsTab = ({ text, id, setValueTab, valueTab }) => { +import BadgeComponent from "../../../../Base/Badge/BadgeComponent"; + +const TicketsTab = ({ text, id, setValueTab, valueTab, count }) => { const [active, setActive] = React.useState(false); const handleClick = ({ target }) => { @@ -11,6 +14,7 @@ const TicketsTab = ({ text, id, setValueTab, valueTab }) => { React.useEffect(() => { valueTab === id ? setActive(true) : setActive(false); }, [valueTab, id]); + return ( { className={active ? "active" : ""} > {text} + {id !== "open" ? ( + + ) : ( + "" + )} ); }; diff --git a/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.jsx b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.jsx index c8a165a..1ede3af 100644 --- a/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.jsx +++ b/frontend/src/components/Ticket/TicketsManager/TicketsTabs/TicketsTabs.jsx @@ -1,16 +1,32 @@ import React from "react"; +import TicketsTab from "../TicketsTabs/TicketsTab/TicketsTab"; import { TicketTabsStyled } from "./TicketsTabs.style"; -import TicketsTab from "./TicketsTab/TicketsTab"; +const TicketsTabs = ({ setValueTab, valueTab, count }) => { -const TicketsTabs = ({ tickets, setValueTab, valueTab }) => { - - if (!setValueTab) return null; return ( - - - + + + ); }; diff --git a/frontend/src/pages/Report/index.js b/frontend/src/pages/Report/index.js index 9912cf2..88986fa 100644 --- a/frontend/src/pages/Report/index.js +++ b/frontend/src/pages/Report/index.js @@ -2,109 +2,97 @@ import React, { useState, useEffect, useReducer, useContext, useRef } from "reac 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 { data } from '../../components/Report/MTable/data'; +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"; -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) const userIndex = state.findIndex((u) => `${u.id}` === `${userId}`); - // console.log('>>>>>>>>>>>>>>>>>>>>> userIndex: ', userIndex) + // console.log('>>>>>>>>>>>>>>>>>>>>> userIndex: ', userIndex) if (userIndex !== -1) { state.splice(userIndex, 1); @@ -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 - } - else { - newQueries.push(query) + state[queryIndex] = 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) + // 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) - - + //console.log(' *********************** index: ', index) if (index !== -1) { - - // console.log('ENTROU NO INDEX') - + // 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 + // 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; } else if (!("sumOpen" in state[index])) { - // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') - state[index].sumOpen = onlineUser.sumOpen + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') + 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 + // 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; } else if (!("sumClosed" in state[index])) { - // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') - state[index].sumClosed = onlineUser.sumClosed + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') + 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 ( (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,76 +239,64 @@ 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: 'Criado', field: 'createdAt' }, + { title: "Status", field: "status" }, + + { 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); - //-------- + //-------- const [searchParam] = useState(""); //const [loading, setLoading] = useState(false); - //const [hasMore, setHasMore] = useState(false); + //const [hasMore, setHasMore] = useState(false); 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 [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 [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); + //setLoading(true); const delayDebounceFn = setTimeout(() => { - const fetchUsers = async () => { try { - //console.log('profile: ', profile) const { data } = await api.get("/users/", { @@ -358,111 +305,87 @@ const Report = () => { dispatch({ type: "LOAD_USERS", payload: data.users }); //setHasMore(data.hasMore); - //setLoading(false); - - + //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); - // console.log('dataQuery: ', dataQuery.data) + // 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" }) + // console.log() + } 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); - // console.log('REPORT 2 dataQuery : ', dataQuery.data) + // 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) - } + // 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,108 +393,87 @@ const Report = () => { // }, [query]) - - - // test del + // test del const handleCSVMessages = () => { - - // setLoading(true); + // 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) + // 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); - + // 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); - // setLoading(true); + 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) - 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) + // 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]; @@ -593,69 +491,78 @@ const Report = () => { // _data.forEach(rd => { // _data = _data.filter(t => t.id !== id); // }); - // setData(_data); + // setData(_data); // }; - useEffect(() => { - - //if (!loading) { - + //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 { await api.get(`/users/logout/${userId}`); //toast.success(("Desloged!")); - //handleDeleteRows(scheduleId) + //handleDeleteRows(scheduleId) } catch (err) { // toastError(err); } - - }; - return ( - ( - - + + + { + return { value: obj.id, label: obj.name }; + })} + /> + - { - return { 'value': obj.id, 'label': obj.name } - })} /> - - - - - + + + + + + + -
      - - {reportOption === '1' && +
      + {reportOption === "1" && (
      - @@ -663,170 +570,129 @@ const Report = () => { + filename={"Relatorio_detalhado_atendimento_atendentes.csv"} + target={"_blank"} + ref={csvLink} + />
      - - } - - + )} - - - - - - - - - - {reportOption === '1' && - - + + {reportOption === "1" && ( + - } - {reportOption === '2' && - + table_title={"Atendimento por atendentes"} + /> + )} + {reportOption === "2" && ( imagem de perfil do whatsapp }, + { title: "Nome", field: "name", cellStyle: { whiteSpace: "nowrap" } }, + + { + title: "Status", + field: "statusOnline.status", + + cellStyle: (e, rowData) => { + if (rowData["statusOnline"] && rowData["statusOnline"].status) { + if (rowData["statusOnline"].status === "offline") { + return { color: "red" }; + } else if (rowData["statusOnline"].status === "online") { + return { color: "green" }; + } else if (rowData["statusOnline"].status === "logout...") { + return { color: "orange" }; + } else if (rowData["statusOnline"].status === "waiting...") { + return { color: "orange" }; + } + } + }, }, - }} - - title="Usuários online/offline" - columns={ - [ - - // { title: 'Foto', field: 'ticket.contact.profilePicUrl', render: rowData => imagem de perfil do whatsapp }, - { title: 'Nome', field: 'name', cellStyle: {whiteSpace: 'nowrap'}, }, - - { - title: 'Status', field: 'statusOnline.status', - - cellStyle: (e, rowData) => { - - if (rowData['statusOnline'] && rowData['statusOnline'].status) { - - if (rowData['statusOnline'].status === 'offline') { - - return { color: "red" }; - } - else if (rowData['statusOnline'].status === 'online') { - return { color: "green" }; - } - else if (rowData['statusOnline'].status === 'logout...') { - return { color: "orange" }; - } - 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) - } - } - - + // console.log(' ROW DATA INFO: ', rowData, ' | rowData: ', rowData.id) + handleLogouOnlineUser(rowData.id); + }, + }; } - } + }, ]} + options={{ + search: true, + selection: false, + paging: false, + padding: "dense", + sorting: true, + //loadingType: 'linear', + searchFieldStyle: { + width: 300, + }, + pageSize: 20, + headerStyle: { + position: "sticky", + top: "0", + }, + maxBodyHeight: "400px", - options={ - { - search: true, - selection: false, - paging: false, - padding: 'dense', - sorting: true, - //loadingType: 'linear', - searchFieldStyle: { - width: 300, - }, + rowStyle: { + fontSize: 14, + }, - pageSize: 20, - headerStyle: { - position: "sticky", - top: "0" - }, - maxBodyHeight: "400px", + // cellStyle: (rowData) => { + // return { + // fontSize: 12, + // color: "#fff", - rowStyle: { - fontSize: 14, - } - - - // cellStyle: (rowData) => { - // return { - // fontSize: 12, - // color: "#fff", - - // }; - // } - - - - - - - - }} + // }; + // } + }} /> - - } - + )} - - )} /> - ) + ); }; export default Report; + diff --git a/frontend/src/style/varibles.jsx b/frontend/src/style/varibles.jsx index 6fc6387..68e8e71 100644 --- a/frontend/src/style/varibles.jsx +++ b/frontend/src/style/varibles.jsx @@ -47,7 +47,7 @@ export const color = { status: { no: "#FF0000", yes: "#00BE1E", - warning: "#FFC700", + warning: "#ffc800c7", }, gradient: { bgOpacity: "#212f3cd7",