mirror of
https://github.com/cheveguerra/whaticket-community.git
synced 2026-04-20 20:59:16 +00:00
Merge pull request #25 from canove/dev
This commit is contained in:
@@ -66,7 +66,7 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
io.to(ticketId).to("notification").emit("appMessage", {
|
io.to(ticketId).to("notification").to(ticket.status).emit("appMessage", {
|
||||||
action: "create",
|
action: "create",
|
||||||
message,
|
message,
|
||||||
ticket,
|
ticket,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type IndexQuery = {
|
|||||||
interface TicketData {
|
interface TicketData {
|
||||||
contactId: number;
|
contactId: number;
|
||||||
status: string;
|
status: string;
|
||||||
|
userId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
@@ -46,12 +47,12 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
const { contactId, status }: TicketData = req.body;
|
const { contactId, status, userId }: TicketData = req.body;
|
||||||
|
|
||||||
const ticket = await CreateTicketService({ contactId, status });
|
const ticket = await CreateTicketService({ contactId, status, userId });
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
io.to("notification").emit("ticket", {
|
io.to(ticket.status).emit("ticket", {
|
||||||
action: "create",
|
action: "create",
|
||||||
ticket
|
ticket
|
||||||
});
|
});
|
||||||
@@ -66,12 +67,24 @@ export const update = async (
|
|||||||
const { ticketId } = req.params;
|
const { ticketId } = req.params;
|
||||||
const ticketData: TicketData = req.body;
|
const ticketData: TicketData = req.body;
|
||||||
|
|
||||||
const ticket = await UpdateTicketService({ ticketData, ticketId });
|
const { ticket, oldStatus, ticketUser } = await UpdateTicketService({
|
||||||
|
ticketData,
|
||||||
|
ticketId
|
||||||
|
});
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
io.to("notification").emit("ticket", {
|
|
||||||
|
if (ticket.status !== oldStatus) {
|
||||||
|
io.to(oldStatus).emit("ticket", {
|
||||||
|
action: "delete",
|
||||||
|
ticketId: ticket.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
io.to(ticket.status).to(ticketId).emit("ticket", {
|
||||||
action: "updateStatus",
|
action: "updateStatus",
|
||||||
ticket
|
ticket,
|
||||||
|
user: ticketUser
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).json(ticket);
|
return res.status(200).json(ticket);
|
||||||
@@ -83,10 +96,12 @@ export const remove = async (
|
|||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const { ticketId } = req.params;
|
const { ticketId } = req.params;
|
||||||
|
|
||||||
await DeleteTicketService(ticketId);
|
const ticket = await DeleteTicketService(ticketId);
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
io.to("notification").emit("ticket", {
|
io.to(ticket.status)
|
||||||
|
.to(ticketId)
|
||||||
|
.emit("ticket", {
|
||||||
action: "delete",
|
action: "delete",
|
||||||
ticketId: +ticketId
|
ticketId: +ticketId
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const SetTicketMessagesAsRead = async (ticket: Ticket): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
io.to("notification").emit("ticket", {
|
io.to(ticket.status).to("notification").emit("ticket", {
|
||||||
action: "updateUnread",
|
action: "updateUnread",
|
||||||
ticketId: ticket.id
|
ticketId: ticket.id
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ export const initIO = (httpServer: Server): SocketIO => {
|
|||||||
socket.join("notification");
|
socket.join("notification");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("joinTickets", status => {
|
||||||
|
console.log(`A client joined to ${status} tickets channel.`);
|
||||||
|
socket.join(status);
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
console.log("Client disconnected");
|
console.log("Client disconnected");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const UpdateContactService = async ({
|
|||||||
|
|
||||||
const contact = await Contact.findOne({
|
const contact = await Contact.findOne({
|
||||||
where: { id: contactId },
|
where: { id: contactId },
|
||||||
attributes: ["id", "name", "number", "email"],
|
attributes: ["id", "name", "number", "email", "profilePicUrl"],
|
||||||
include: ["extraInfo"]
|
include: ["extraInfo"]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const CreateMessageService = async ({
|
|||||||
const ticket = await ShowTicketService(ticketId);
|
const ticket = await ShowTicketService(ticketId);
|
||||||
|
|
||||||
if (!ticket) {
|
if (!ticket) {
|
||||||
throw new AppError("No ticket found with this ID");
|
throw new AppError("No ticket found with this ID", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
const message: Message = await ticket.$create("message", messageData);
|
const message: Message = await ticket.$create("message", messageData);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { where, fn, col } from "sequelize";
|
import { where, fn, col } from "sequelize";
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
import Message from "../../models/Message";
|
import Message from "../../models/Message";
|
||||||
import Ticket from "../../models/Ticket";
|
import Ticket from "../../models/Ticket";
|
||||||
import ShowTicketService from "../TicketServices/ShowTicketService";
|
import ShowTicketService from "../TicketServices/ShowTicketService";
|
||||||
@@ -24,7 +25,7 @@ const ListMessagesService = async ({
|
|||||||
const ticket = await ShowTicketService(ticketId);
|
const ticket = await ShowTicketService(ticketId);
|
||||||
|
|
||||||
if (!ticket) {
|
if (!ticket) {
|
||||||
throw new Error("No ticket found with this ID");
|
throw new AppError("No ticket found with this ID", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
const whereCondition = {
|
const whereCondition = {
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import Ticket from "../../models/Ticket";
|
|||||||
|
|
||||||
interface Request {
|
interface Request {
|
||||||
contactId: number;
|
contactId: number;
|
||||||
status?: string;
|
status: string;
|
||||||
|
userId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateTicketService = async ({
|
const CreateTicketService = async ({
|
||||||
contactId,
|
contactId,
|
||||||
status
|
status,
|
||||||
|
userId
|
||||||
}: Request): Promise<Ticket> => {
|
}: Request): Promise<Ticket> => {
|
||||||
const defaultWhatsapp = await GetDefaultWhatsApp();
|
const defaultWhatsapp = await GetDefaultWhatsApp();
|
||||||
|
|
||||||
@@ -19,7 +21,8 @@ const CreateTicketService = async ({
|
|||||||
|
|
||||||
const { id }: Ticket = await defaultWhatsapp.$create("ticket", {
|
const { id }: Ticket = await defaultWhatsapp.$create("ticket", {
|
||||||
contactId,
|
contactId,
|
||||||
status
|
status,
|
||||||
|
userId
|
||||||
});
|
});
|
||||||
|
|
||||||
const ticket = await Ticket.findByPk(id, { include: ["contact"] });
|
const ticket = await Ticket.findByPk(id, { include: ["contact"] });
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Ticket from "../../models/Ticket";
|
import Ticket from "../../models/Ticket";
|
||||||
import AppError from "../../errors/AppError";
|
import AppError from "../../errors/AppError";
|
||||||
|
|
||||||
const DeleteTicketService = async (id: string): Promise<void> => {
|
const DeleteTicketService = async (id: string): Promise<Ticket> => {
|
||||||
const ticket = await Ticket.findOne({
|
const ticket = await Ticket.findOne({
|
||||||
where: { id }
|
where: { id }
|
||||||
});
|
});
|
||||||
@@ -11,6 +11,8 @@ const DeleteTicketService = async (id: string): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ticket.destroy();
|
await ticket.destroy();
|
||||||
|
|
||||||
|
return ticket;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeleteTicketService;
|
export default DeleteTicketService;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const ShowTicketService = async (id: string | number): Promise<Ticket> => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!ticket) {
|
if (!ticket) {
|
||||||
throw new AppError("No ticket found with this ID");
|
throw new AppError("No ticket found with this ID", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ticket;
|
return ticket;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import AppError from "../../errors/AppError";
|
import AppError from "../../errors/AppError";
|
||||||
import Contact from "../../models/Contact";
|
import Contact from "../../models/Contact";
|
||||||
import Ticket from "../../models/Ticket";
|
import Ticket from "../../models/Ticket";
|
||||||
|
import User from "../../models/User";
|
||||||
|
|
||||||
interface TicketData {
|
interface TicketData {
|
||||||
status?: string;
|
status?: string;
|
||||||
@@ -12,10 +13,16 @@ interface Request {
|
|||||||
ticketId: string;
|
ticketId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Response {
|
||||||
|
ticket: Ticket;
|
||||||
|
ticketUser: User | null;
|
||||||
|
oldStatus: string;
|
||||||
|
}
|
||||||
|
|
||||||
const UpdateTicketService = async ({
|
const UpdateTicketService = async ({
|
||||||
ticketData,
|
ticketData,
|
||||||
ticketId
|
ticketId
|
||||||
}: Request): Promise<Ticket> => {
|
}: Request): Promise<Response> => {
|
||||||
const { status, userId } = ticketData;
|
const { status, userId } = ticketData;
|
||||||
|
|
||||||
const ticket = await Ticket.findOne({
|
const ticket = await Ticket.findOne({
|
||||||
@@ -33,12 +40,15 @@ const UpdateTicketService = async ({
|
|||||||
throw new AppError("No ticket found with this ID.", 404);
|
throw new AppError("No ticket found with this ID.", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const oldStatus = ticket.status;
|
||||||
|
|
||||||
await ticket.update({
|
await ticket.update({
|
||||||
status,
|
status,
|
||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
|
const ticketUser = await ticket.$get("user", { attributes: ["id", "name"] });
|
||||||
|
|
||||||
return ticket;
|
return { ticket, oldStatus, ticketUser };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UpdateTicketService;
|
export default UpdateTicketService;
|
||||||
|
|||||||
@@ -138,7 +138,10 @@ const handleMessage = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
io.to(ticket.id.toString()).to("notification").emit("appMessage", {
|
io.to(ticket.id.toString())
|
||||||
|
.to(ticket.status)
|
||||||
|
.to("notification")
|
||||||
|
.emit("appMessage", {
|
||||||
action: "create",
|
action: "create",
|
||||||
message: newMessage,
|
message: newMessage,
|
||||||
ticket,
|
ticket,
|
||||||
|
|||||||
23
frontend/src/components/BackdropLoading/index.js
Normal file
23
frontend/src/components/BackdropLoading/index.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import Backdrop from "@material-ui/core/Backdrop";
|
||||||
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(theme => ({
|
||||||
|
backdrop: {
|
||||||
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const BackdropLoading = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
return (
|
||||||
|
<Backdrop className={classes.backdrop} open={true}>
|
||||||
|
<CircularProgress color="inherit" />
|
||||||
|
</Backdrop>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BackdropLoading;
|
||||||
@@ -293,11 +293,13 @@ const MessagesList = () => {
|
|||||||
const { data } = await api.get("/messages/" + ticketId, {
|
const { data } = await api.get("/messages/" + ticketId, {
|
||||||
params: { pageNumber },
|
params: { pageNumber },
|
||||||
});
|
});
|
||||||
|
|
||||||
setContact(data.ticket.contact);
|
setContact(data.ticket.contact);
|
||||||
setTicket(data.ticket);
|
setTicket(data.ticket);
|
||||||
dispatch({ type: "LOAD_MESSAGES", payload: data.messages });
|
dispatch({ type: "LOAD_MESSAGES", payload: data.messages });
|
||||||
setHasMore(data.hasMore);
|
setHasMore(data.hasMore);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
if (pageNumber === 1 && data.messages.length > 1) {
|
if (pageNumber === 1 && data.messages.length > 1) {
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
@@ -318,7 +320,7 @@ const MessagesList = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||||
socket.emit("joinChatBox", ticketId, () => {});
|
socket.emit("joinChatBox", ticketId);
|
||||||
|
|
||||||
socket.on("appMessage", data => {
|
socket.on("appMessage", data => {
|
||||||
if (data.action === "create") {
|
if (data.action === "create") {
|
||||||
@@ -330,6 +332,17 @@ const MessagesList = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("ticket", data => {
|
||||||
|
if (data.action === "updateStatus") {
|
||||||
|
setTicket({ ...data.ticket, user: data.user });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.action === "delete") {
|
||||||
|
toast.success("Ticket deleted sucessfully.");
|
||||||
|
history.push("/tickets");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("contact", data => {
|
socket.on("contact", data => {
|
||||||
if (data.action === "update") {
|
if (data.action === "update") {
|
||||||
setContact(data.contact);
|
setContact(data.contact);
|
||||||
@@ -339,7 +352,7 @@ const MessagesList = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
};
|
};
|
||||||
}, [ticketId]);
|
}, [ticketId, history]);
|
||||||
|
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
setPageNumber(prevPageNumber => prevPageNumber + 1);
|
setPageNumber(prevPageNumber => prevPageNumber + 1);
|
||||||
@@ -585,11 +598,12 @@ const MessagesList = () => {
|
|||||||
subheader={
|
subheader={
|
||||||
loading ? (
|
loading ? (
|
||||||
<Skeleton animation="wave" width={80} />
|
<Skeleton animation="wave" width={80} />
|
||||||
) : (
|
) : ticket.user ? (
|
||||||
ticket.user &&
|
|
||||||
`${i18n.t("messagesList.header.assignedTo")} ${
|
`${i18n.t("messagesList.header.assignedTo")} ${
|
||||||
ticket.user.name
|
ticket.user.name
|
||||||
}`
|
}`
|
||||||
|
) : (
|
||||||
|
"Pending"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useRef, useCallback, useEffect } from "react";
|
import React, { useState, useRef, useCallback, useEffect } from "react";
|
||||||
|
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import openSocket from "socket.io-client";
|
import openSocket from "socket.io-client";
|
||||||
|
|
||||||
@@ -13,11 +14,7 @@ import Badge from "@material-ui/core/Badge";
|
|||||||
import ChatIcon from "@material-ui/icons/Chat";
|
import ChatIcon from "@material-ui/icons/Chat";
|
||||||
|
|
||||||
import TicketListItem from "../TicketListItem";
|
import TicketListItem from "../TicketListItem";
|
||||||
|
|
||||||
// import { toast } from "react-toastify";
|
|
||||||
import { useHistory } from "react-router-dom";
|
|
||||||
import { i18n } from "../../translate/i18n";
|
import { i18n } from "../../translate/i18n";
|
||||||
|
|
||||||
import useTickets from "../../hooks/useTickets";
|
import useTickets from "../../hooks/useTickets";
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
@@ -46,10 +43,13 @@ const NotificationsPopOver = () => {
|
|||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const userId = +localStorage.getItem("userId");
|
const userId = +localStorage.getItem("userId");
|
||||||
const soundAlert = useRef(new Audio(require("../../assets/sound.mp3")));
|
const soundAlert = useRef(new Audio(require("../../assets/sound.mp3")));
|
||||||
const ticketId = +history.location.pathname.split("/")[2];
|
const ticketIdUrl = +history.location.pathname.split("/")[2];
|
||||||
|
const ticketIdRef = useRef(ticketIdUrl);
|
||||||
const anchorEl = useRef();
|
const anchorEl = useRef();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
// const [notifications, setNotifications] = useState([]);
|
const [notifications, setNotifications] = useState([]);
|
||||||
|
|
||||||
|
const { tickets } = useTickets({ withUnreadMessages: "true" });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!("Notification" in window)) {
|
if (!("Notification" in window)) {
|
||||||
@@ -59,31 +59,56 @@ const NotificationsPopOver = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setNotifications(tickets);
|
||||||
|
}, [tickets]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ticketIdRef.current = ticketIdUrl;
|
||||||
|
}, [ticketIdUrl]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||||
|
|
||||||
socket.emit("joinNotification");
|
socket.emit("joinNotification");
|
||||||
|
|
||||||
|
socket.on("ticket", data => {
|
||||||
|
if (data.action === "updateUnread") {
|
||||||
|
setNotifications(prevState => {
|
||||||
|
const ticketIndex = prevState.findIndex(t => t.id === data.ticketId);
|
||||||
|
if (ticketIndex !== -1) {
|
||||||
|
prevState.splice(ticketIndex, 1);
|
||||||
|
return [...prevState];
|
||||||
|
}
|
||||||
|
return prevState;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("appMessage", data => {
|
socket.on("appMessage", data => {
|
||||||
if (data.action === "create") {
|
|
||||||
if (
|
if (
|
||||||
(ticketId &&
|
data.action === "create" &&
|
||||||
data.message.ticketId === +ticketId &&
|
(data.ticket.userId === userId || !data.ticket.userId)
|
||||||
|
) {
|
||||||
|
setNotifications(prevState => {
|
||||||
|
const ticketIndex = prevState.findIndex(t => t.id === data.ticket.id);
|
||||||
|
if (ticketIndex !== -1) {
|
||||||
|
prevState[ticketIndex] = data.ticket;
|
||||||
|
return [...prevState];
|
||||||
|
}
|
||||||
|
return [data.ticket, ...prevState];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
(ticketIdRef.current &&
|
||||||
|
data.message.ticketId === ticketIdRef.current &&
|
||||||
document.visibilityState === "visible") ||
|
document.visibilityState === "visible") ||
|
||||||
(data.ticket.userId !== userId && data.ticket.userId)
|
(data.ticket.userId !== userId && data.ticket.userId)
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
showDesktopNotification(data);
|
else {
|
||||||
}
|
// show desktop notification
|
||||||
});
|
const { message, contact, ticket } = data;
|
||||||
|
|
||||||
return () => {
|
|
||||||
socket.disconnect();
|
|
||||||
};
|
|
||||||
}, [history, ticketId, userId]);
|
|
||||||
|
|
||||||
const { tickets: notifications } = useTickets({ withUnreadMessages: "true" });
|
|
||||||
|
|
||||||
const showDesktopNotification = ({ message, contact, ticket }) => {
|
|
||||||
const options = {
|
const options = {
|
||||||
body: `${message.body} - ${format(new Date(), "HH:mm")}`,
|
body: `${message.body} - ${format(new Date(), "HH:mm")}`,
|
||||||
icon: contact.profilePicUrl,
|
icon: contact.profilePicUrl,
|
||||||
@@ -96,7 +121,8 @@ const NotificationsPopOver = () => {
|
|||||||
|
|
||||||
notification.onclick = function (event) {
|
notification.onclick = function (event) {
|
||||||
event.preventDefault(); //
|
event.preventDefault(); //
|
||||||
window.open(`/tickets/${ticket.id}`, "_self");
|
window.focus();
|
||||||
|
history.push(`/tickets/${ticket.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener("visibilitychange", () => {
|
document.addEventListener("visibilitychange", () => {
|
||||||
@@ -106,7 +132,14 @@ const NotificationsPopOver = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
soundAlert.current.play();
|
soundAlert.current.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.disconnect();
|
||||||
};
|
};
|
||||||
|
}, [history, userId]);
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
setIsOpen(!isOpen);
|
setIsOpen(!isOpen);
|
||||||
@@ -153,8 +186,6 @@ const NotificationsPopOver = () => {
|
|||||||
<ListItemText>No tickets with unread messages.</ListItemText>
|
<ListItemText>No tickets with unread messages.</ListItemText>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
) : (
|
) : (
|
||||||
notifications &&
|
|
||||||
notifications.length > 0 &&
|
|
||||||
notifications.map(ticket => (
|
notifications.map(ticket => (
|
||||||
<NotificationTicket key={ticket.id}>
|
<NotificationTicket key={ticket.id}>
|
||||||
<TicketListItem ticket={ticket} />
|
<TicketListItem ticket={ticket} />
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useHistory } from "react-router-dom";
|
|
||||||
|
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
@@ -12,13 +11,10 @@ import ConfirmationModal from "../ConfirmationModal";
|
|||||||
|
|
||||||
const TicketOptionsMenu = ({ ticket, menuOpen, handleClose, anchorEl }) => {
|
const TicketOptionsMenu = ({ ticket, menuOpen, handleClose, anchorEl }) => {
|
||||||
const [confirmationOpen, setConfirmationOpen] = useState(false);
|
const [confirmationOpen, setConfirmationOpen] = useState(false);
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const handleDeleteTicket = async () => {
|
const handleDeleteTicket = async () => {
|
||||||
try {
|
try {
|
||||||
await api.delete(`/tickets/${ticket.id}`);
|
await api.delete(`/tickets/${ticket.id}`);
|
||||||
toast.success("Ticket deletado com sucesso.");
|
|
||||||
history.push("/tickets");
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error("Erro ao deletar o ticket");
|
toast.error("Erro ao deletar o ticket");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useReducer } from "react";
|
||||||
|
import openSocket from "socket.io-client";
|
||||||
|
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import List from "@material-ui/core/List";
|
import List from "@material-ui/core/List";
|
||||||
@@ -69,11 +70,97 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
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_MESSAGES_COUNT") {
|
||||||
|
const ticket = action.payload;
|
||||||
|
|
||||||
|
const ticketIndex = state.findIndex(t => t.id === ticket.id);
|
||||||
|
if (ticketIndex !== -1) {
|
||||||
|
state[ticketIndex] = ticket;
|
||||||
|
state.unshift(state.splice(ticketIndex, 1)[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 }) => {
|
const TicketsList = ({ status, searchParam, showAll }) => {
|
||||||
|
const userId = +localStorage.getItem("userId");
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [pageNumber, setPageNumber] = useState(1);
|
const [pageNumber, setPageNumber] = useState(1);
|
||||||
|
const [ticketsList, dispatch] = useReducer(reducer, []);
|
||||||
|
|
||||||
const { tickets, hasMore, loading, dispatch } = useTickets({
|
useEffect(() => {
|
||||||
|
dispatch({ type: "RESET" });
|
||||||
|
setPageNumber(1);
|
||||||
|
}, [status, searchParam, dispatch, showAll]);
|
||||||
|
|
||||||
|
const { tickets, hasMore, loading } = useTickets({
|
||||||
pageNumber,
|
pageNumber,
|
||||||
searchParam,
|
searchParam,
|
||||||
status,
|
status,
|
||||||
@@ -81,9 +168,61 @@ const TicketsList = ({ status, searchParam, showAll }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch({ type: "RESET" });
|
dispatch({
|
||||||
setPageNumber(1);
|
type: "LOAD_TICKETS",
|
||||||
}, [status, searchParam, dispatch, showAll]);
|
payload: tickets,
|
||||||
|
});
|
||||||
|
}, [tickets]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||||
|
socket.emit("joinTickets", status);
|
||||||
|
|
||||||
|
socket.on("ticket", data => {
|
||||||
|
if (data.action === "updateUnread") {
|
||||||
|
dispatch({
|
||||||
|
type: "RESET_UNREAD",
|
||||||
|
payload: data.ticketId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(data.action === "updateStatus" || data.action === "create") &&
|
||||||
|
(!data.ticket.userId || data.ticket.userId === userId || showAll)
|
||||||
|
) {
|
||||||
|
dispatch({
|
||||||
|
type: "UPDATE_TICKET",
|
||||||
|
payload: data.ticket,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.action === "delete") {
|
||||||
|
dispatch({ type: "DELETE_TICKET", payload: data.ticketId });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("appMessage", data => {
|
||||||
|
if (data.action === "create") {
|
||||||
|
dispatch({
|
||||||
|
type: "UPDATE_TICKET_MESSAGES_COUNT",
|
||||||
|
payload: data.ticket,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("contact", data => {
|
||||||
|
if (data.action === "update") {
|
||||||
|
dispatch({
|
||||||
|
type: "UPDATE_TICKET_CONTACT",
|
||||||
|
payload: data.contact,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.disconnect();
|
||||||
|
};
|
||||||
|
}, [status, showAll, userId]);
|
||||||
|
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
setPageNumber(prevState => prevState + 1);
|
setPageNumber(prevState => prevState + 1);
|
||||||
@@ -91,7 +230,9 @@ const TicketsList = ({ status, searchParam, showAll }) => {
|
|||||||
|
|
||||||
const handleScroll = e => {
|
const handleScroll = e => {
|
||||||
if (!hasMore || loading) return;
|
if (!hasMore || loading) return;
|
||||||
|
|
||||||
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
||||||
|
|
||||||
if (scrollHeight - (scrollTop + 100) < clientHeight) {
|
if (scrollHeight - (scrollTop + 100) < clientHeight) {
|
||||||
loadMore();
|
loadMore();
|
||||||
}
|
}
|
||||||
@@ -110,16 +251,16 @@ const TicketsList = ({ status, searchParam, showAll }) => {
|
|||||||
{status === "open" && (
|
{status === "open" && (
|
||||||
<div className={classes.ticketsListHeader}>
|
<div className={classes.ticketsListHeader}>
|
||||||
{i18n.t("ticketsList.assignedHeader")}
|
{i18n.t("ticketsList.assignedHeader")}
|
||||||
<span className={classes.ticketsCount}>{tickets.length}</span>
|
<span className={classes.ticketsCount}>{ticketsList.length}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{status === "pending" && (
|
{status === "pending" && (
|
||||||
<div className={classes.ticketsListHeader}>
|
<div className={classes.ticketsListHeader}>
|
||||||
{i18n.t("ticketsList.pendingHeader")}
|
{i18n.t("ticketsList.pendingHeader")}
|
||||||
<span className={classes.ticketsCount}>{tickets.length}</span>
|
<span className={classes.ticketsCount}>{ticketsList.length}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{tickets.length === 0 && !loading ? (
|
{ticketsList.length === 0 && !loading ? (
|
||||||
<div className={classes.noTicketsDiv}>
|
<div className={classes.noTicketsDiv}>
|
||||||
<span className={classes.noTicketsTitle}>
|
<span className={classes.noTicketsTitle}>
|
||||||
{i18n.t("ticketsList.noTicketsTitle")}
|
{i18n.t("ticketsList.noTicketsTitle")}
|
||||||
@@ -130,7 +271,7 @@ const TicketsList = ({ status, searchParam, showAll }) => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{tickets.map(ticket => (
|
{ticketsList.map(ticket => (
|
||||||
<TicketListItem ticket={ticket} key={ticket.id} />
|
<TicketListItem ticket={ticket} key={ticket.id} />
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ import AccountCircle from "@material-ui/icons/AccountCircle";
|
|||||||
|
|
||||||
import MainListItems from "./MainListItems";
|
import MainListItems from "./MainListItems";
|
||||||
import NotificationsPopOver from "../NotificationsPopOver";
|
import NotificationsPopOver from "../NotificationsPopOver";
|
||||||
|
import UserModal from "../UserModal";
|
||||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||||
|
import BackdropLoading from "../BackdropLoading";
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth = 240;
|
||||||
|
|
||||||
@@ -100,13 +102,15 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const MainDrawer = ({ appTitle, children }) => {
|
const LoggedInLayout = ({ appTitle, children }) => {
|
||||||
const { handleLogout } = useContext(AuthContext);
|
const drawerState = localStorage.getItem("drawerOpen");
|
||||||
|
const userId = +localStorage.getItem("userId");
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [open, setOpen] = useState(true);
|
const [open, setOpen] = useState(true);
|
||||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
const [userModalOpen, setUserModalOpen] = useState(false);
|
||||||
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
const drawerState = localStorage.getItem("drawerOpen");
|
const { handleLogout, loading } = useContext(AuthContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (drawerState === "0") {
|
if (drawerState === "0") {
|
||||||
@@ -134,6 +138,15 @@ const MainDrawer = ({ appTitle, children }) => {
|
|||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenUserModal = () => {
|
||||||
|
setUserModalOpen(true);
|
||||||
|
handleCloseMenu();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <BackdropLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<Drawer
|
<Drawer
|
||||||
@@ -154,6 +167,11 @@ const MainDrawer = ({ appTitle, children }) => {
|
|||||||
</List>
|
</List>
|
||||||
<Divider />
|
<Divider />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
<UserModal
|
||||||
|
open={userModalOpen}
|
||||||
|
onClose={() => setUserModalOpen(false)}
|
||||||
|
userId={userId}
|
||||||
|
/>
|
||||||
<AppBar
|
<AppBar
|
||||||
position="absolute"
|
position="absolute"
|
||||||
className={clsx(classes.appBar, open && classes.appBarShift)}
|
className={clsx(classes.appBar, open && classes.appBarShift)}
|
||||||
@@ -179,7 +197,7 @@ const MainDrawer = ({ appTitle, children }) => {
|
|||||||
noWrap
|
noWrap
|
||||||
className={classes.title}
|
className={classes.title}
|
||||||
>
|
>
|
||||||
{appTitle}
|
WHATICKET
|
||||||
</Typography>
|
</Typography>
|
||||||
<NotificationsPopOver />
|
<NotificationsPopOver />
|
||||||
|
|
||||||
@@ -208,7 +226,7 @@ const MainDrawer = ({ appTitle, children }) => {
|
|||||||
open={menuOpen}
|
open={menuOpen}
|
||||||
onClose={handleCloseMenu}
|
onClose={handleCloseMenu}
|
||||||
>
|
>
|
||||||
<MenuItem onClick={handleCloseMenu}>Profile</MenuItem>
|
<MenuItem onClick={handleOpenUserModal}>Profile</MenuItem>
|
||||||
<MenuItem onClick={handleLogout}>Logout</MenuItem>
|
<MenuItem onClick={handleLogout}>Logout</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
@@ -223,4 +241,4 @@ const MainDrawer = ({ appTitle, children }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MainDrawer;
|
export default LoggedInLayout;
|
||||||
|
|||||||
@@ -1,80 +1,8 @@
|
|||||||
import { useState, useEffect, useReducer } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import openSocket from "socket.io-client";
|
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
import api from "../../services/api";
|
import api from "../../services/api";
|
||||||
|
|
||||||
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 === "UPDATE_TICKETS") {
|
|
||||||
const { ticket, status, loggedUser, withUnreadMessages } = action.payload;
|
|
||||||
|
|
||||||
const ticketIndex = state.findIndex(t => t.id === ticket.id);
|
|
||||||
if (ticketIndex !== -1) {
|
|
||||||
if (ticket.status !== state[ticketIndex].status) {
|
|
||||||
state.splice(ticketIndex, 1);
|
|
||||||
} else {
|
|
||||||
state[ticketIndex] = ticket;
|
|
||||||
state.unshift(state.splice(ticketIndex, 1)[0]);
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
ticket.status === status &&
|
|
||||||
(ticket.userId === loggedUser ||
|
|
||||||
!ticket.userId ||
|
|
||||||
ticket.status === "closed")
|
|
||||||
) {
|
|
||||||
state.unshift(ticket);
|
|
||||||
} else if (withUnreadMessages) {
|
|
||||||
state.unshift(ticket);
|
|
||||||
}
|
|
||||||
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_UNREAD") {
|
|
||||||
const { ticketId, withUnreadMessages } = action.payload;
|
|
||||||
|
|
||||||
const ticketIndex = state.findIndex(t => t.id === ticketId);
|
|
||||||
if (ticketIndex !== -1) {
|
|
||||||
state[ticketIndex].unreadMessages = 0;
|
|
||||||
if (withUnreadMessages) {
|
|
||||||
state.splice(ticketIndex, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [...state];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.type === "RESET") {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const useTickets = ({
|
const useTickets = ({
|
||||||
searchParam,
|
searchParam,
|
||||||
pageNumber,
|
pageNumber,
|
||||||
@@ -83,10 +11,9 @@ const useTickets = ({
|
|||||||
showAll,
|
showAll,
|
||||||
withUnreadMessages,
|
withUnreadMessages,
|
||||||
}) => {
|
}) => {
|
||||||
const userId = +localStorage.getItem("userId");
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [hasMore, setHasMore] = useState(false);
|
const [hasMore, setHasMore] = useState(false);
|
||||||
const [tickets, dispatch] = useReducer(reducer, []);
|
const [tickets, setTickets] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -103,10 +30,7 @@ const useTickets = ({
|
|||||||
withUnreadMessages,
|
withUnreadMessages,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
dispatch({
|
setTickets(data.tickets);
|
||||||
type: "LOAD_TICKETS",
|
|
||||||
payload: data.tickets,
|
|
||||||
});
|
|
||||||
setHasMore(data.hasMore);
|
setHasMore(data.hasMore);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -121,57 +45,7 @@ const useTickets = ({
|
|||||||
return () => clearTimeout(delayDebounceFn);
|
return () => clearTimeout(delayDebounceFn);
|
||||||
}, [searchParam, pageNumber, status, date, showAll, withUnreadMessages]);
|
}, [searchParam, pageNumber, status, date, showAll, withUnreadMessages]);
|
||||||
|
|
||||||
useEffect(() => {
|
return { tickets, loading, hasMore };
|
||||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
|
||||||
socket.emit("joinNotification");
|
|
||||||
|
|
||||||
socket.on("ticket", data => {
|
|
||||||
if (data.action === "updateUnread") {
|
|
||||||
dispatch({
|
|
||||||
type: "RESET_UNREAD",
|
|
||||||
payload: {
|
|
||||||
ticketId: data.ticketId,
|
|
||||||
withUnreadMessages: withUnreadMessages,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.action === "updateStatus" || data.action === "create") {
|
|
||||||
dispatch({
|
|
||||||
type: "UPDATE_TICKETS",
|
|
||||||
payload: {
|
|
||||||
ticket: data.ticket,
|
|
||||||
status: status,
|
|
||||||
loggedUser: userId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.action === "delete") {
|
|
||||||
dispatch({ type: "DELETE_TICKET", payload: data.ticketId });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("appMessage", data => {
|
|
||||||
if (data.action === "create") {
|
|
||||||
dispatch({
|
|
||||||
type: "UPDATE_TICKETS",
|
|
||||||
payload: {
|
|
||||||
ticket: data.ticket,
|
|
||||||
status: status,
|
|
||||||
withUnreadMessages: withUnreadMessages,
|
|
||||||
loggedUser: userId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
socket.disconnect();
|
|
||||||
};
|
|
||||||
}, [status, withUnreadMessages, userId]);
|
|
||||||
|
|
||||||
return { loading, tickets, hasMore, dispatch };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useTickets;
|
export default useTickets;
|
||||||
|
|||||||
@@ -167,8 +167,8 @@ const Connections = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseQrModal = useCallback(() => {
|
const handleCloseQrModal = useCallback(() => {
|
||||||
setQrModalOpen(false);
|
|
||||||
setSelectedWhatsApp(null);
|
setSelectedWhatsApp(null);
|
||||||
|
setQrModalOpen(false);
|
||||||
}, [setQrModalOpen, setSelectedWhatsApp]);
|
}, [setQrModalOpen, setSelectedWhatsApp]);
|
||||||
|
|
||||||
const handleEditWhatsApp = whatsApp => {
|
const handleEditWhatsApp = whatsApp => {
|
||||||
|
|||||||
@@ -1,29 +1,13 @@
|
|||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import { Route, Redirect } from "react-router-dom";
|
import { Route, Redirect } from "react-router-dom";
|
||||||
|
|
||||||
import Backdrop from "@material-ui/core/Backdrop";
|
|
||||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
|
||||||
|
|
||||||
import { AuthContext } from "../context/Auth/AuthContext";
|
import { AuthContext } from "../context/Auth/AuthContext";
|
||||||
|
import BackdropLoading from "../components/BackdropLoading";
|
||||||
const useStyles = makeStyles(theme => ({
|
|
||||||
backdrop: {
|
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
|
||||||
color: "#fff",
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const RouteWrapper = ({ component: Component, isPrivate = false, ...rest }) => {
|
const RouteWrapper = ({ component: Component, isPrivate = false, ...rest }) => {
|
||||||
const classes = useStyles();
|
|
||||||
const { isAuth, loading } = useContext(AuthContext);
|
const { isAuth, loading } = useContext(AuthContext);
|
||||||
|
|
||||||
if (loading)
|
if (loading) return <BackdropLoading />;
|
||||||
return (
|
|
||||||
<Backdrop className={classes.backdrop} open={loading}>
|
|
||||||
<CircularProgress color="inherit" />
|
|
||||||
</Backdrop>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isAuth && isPrivate) {
|
if (!isAuth && isPrivate) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import { BrowserRouter, Switch } from "react-router-dom";
|
import { BrowserRouter, Switch } from "react-router-dom";
|
||||||
import { ToastContainer } from "react-toastify";
|
import { ToastContainer } from "react-toastify";
|
||||||
|
|
||||||
import MainDrawer from "../components/_layout";
|
import LoggedInLayout from "../components/_layout";
|
||||||
import Dashboard from "../pages/Dashboard/";
|
import Dashboard from "../pages/Dashboard/";
|
||||||
import Tickets from "../pages/Tickets/";
|
import Tickets from "../pages/Tickets/";
|
||||||
import Signup from "../pages/Signup/";
|
import Signup from "../pages/Signup/";
|
||||||
@@ -21,7 +21,7 @@ const Routes = () => {
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/login" component={Login} />
|
<Route exact path="/login" component={Login} />
|
||||||
<Route exact path="/signup" component={Signup} />
|
<Route exact path="/signup" component={Signup} />
|
||||||
<MainDrawer>
|
<LoggedInLayout>
|
||||||
<Route exact path="/" component={Dashboard} isPrivate />
|
<Route exact path="/" component={Dashboard} isPrivate />
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
@@ -38,7 +38,7 @@ const Routes = () => {
|
|||||||
<Route exact path="/contacts" component={Contacts} isPrivate />
|
<Route exact path="/contacts" component={Contacts} isPrivate />
|
||||||
<Route exact path="/users" component={Users} isPrivate />
|
<Route exact path="/users" component={Users} isPrivate />
|
||||||
<Route exact path="/Settings" component={Settings} isPrivate />
|
<Route exact path="/Settings" component={Settings} isPrivate />
|
||||||
</MainDrawer>
|
</LoggedInLayout>
|
||||||
</Switch>
|
</Switch>
|
||||||
<ToastContainer autoClose={3000} />
|
<ToastContainer autoClose={3000} />
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user