diff --git a/frontend/src/assets/images/User/clientDefault.png b/frontend/src/assets/images/User/clientDefault.png
new file mode 100644
index 0000000..75dbece
Binary files /dev/null and b/frontend/src/assets/images/User/clientDefault.png differ
diff --git a/frontend/src/components/Base/Badge/BadgeComponent.jsx b/frontend/src/components/Base/Badge/BadgeComponent.jsx
new file mode 100644
index 0000000..93e1b35
--- /dev/null
+++ b/frontend/src/components/Base/Badge/BadgeComponent.jsx
@@ -0,0 +1,13 @@
+import React from "react";
+import { BadgeComponentStyled } from "./BadgeComponent.style";
+
+const BadgeComponent = ({ counter, position, top, left, right, bottom }) => {
+ 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 (
-