diff --git a/.github/stale.yml b/.github/stale.yml index 0827505..394a813 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -7,7 +7,7 @@ exemptLabels: - pinned - security - bug - - enchantment + - enhancement # Label to use when marking an issue as stale staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable diff --git a/frontend/src/components/MessagesList/index.js b/frontend/src/components/Ticket/index.js similarity index 77% rename from frontend/src/components/MessagesList/index.js rename to frontend/src/components/Ticket/index.js index 5db3271..841d81b 100644 --- a/frontend/src/components/MessagesList/index.js +++ b/frontend/src/components/Ticket/index.js @@ -30,6 +30,9 @@ import whatsBackground from "../../assets/wa-background.png"; import LinkifyWithTargetBlank from "../LinkifyWithTargetBlank"; import MessageInput from "../MessageInput/"; import TicketOptionsMenu from "../TicketOptionsMenu"; +import TicketHeader from "../TicketHeader"; +import TicketInfo from "../TicketInfo"; +import TicketActionButtons from "../TicketActionButtons"; const drawerWidth = 320; @@ -67,24 +70,6 @@ const useStyles = makeStyles(theme => ({ marginRight: 0, }, - messagesHeader: { - display: "flex", - // cursor: "pointer", - backgroundColor: "#eee", - flex: "none", - borderBottom: "1px solid rgba(0, 0, 0, 0.12)", - }, - - actionButtons: { - marginRight: 6, - flex: "none", - alignSelf: "center", - marginLeft: "auto", - "& > *": { - margin: theme.spacing(1), - }, - }, - messagesListWrapper: { overflow: "hidden", position: "relative", @@ -262,14 +247,13 @@ const reducer = (state, action) => { } }; -const MessagesList = () => { +const Ticket = () => { const { ticketId } = useParams(); const history = useHistory(); const classes = useStyles(); - const userId = +localStorage.getItem("userId"); - const [loading, setLoading] = useState(true); + const [ticketLoading, setTicketLoading] = useState(true); const [contact, setContact] = useState({}); const [ticket, setTicket] = useState({}); const [drawerOpen, setDrawerOpen] = useState(false); @@ -279,12 +263,10 @@ const MessagesList = () => { const [hasMore, setHasMore] = useState(false); const [pageNumber, setPageNumber] = useState(1); - const [anchorEl, setAnchorEl] = useState(null); - const moreMenuOpen = Boolean(anchorEl); - useEffect(() => { dispatch({ type: "RESET" }); setPageNumber(1); + setTicketLoading(true); }, [ticketId]); useEffect(() => { @@ -301,6 +283,7 @@ const MessagesList = () => { dispatch({ type: "LOAD_MESSAGES", payload: data.messages }); setHasMore(data.hasMore); setLoading(false); + setTicketLoading(false); if (pageNumber === 1 && data.messages.length > 1) { scrollToBottom(); @@ -420,29 +403,6 @@ const MessagesList = () => { } }; - const handleOpenTicketOptionsMenu = e => { - setAnchorEl(e.currentTarget); - }; - - const handleCloseTicketOptionsMenu = e => { - setAnchorEl(null); - }; - - const handleUpdateTicketStatus = async (e, status, userId) => { - try { - await api.put(`/tickets/${ticketId}`, { - status: status, - userId: userId || null, - }); - } catch (err) { - console.log(err); - if (err.response && err.response.data && err.response.data.error) { - toast.error(err.response.data.error); - } - } - history.push("/tickets"); - }; - const handleDrawerOpen = () => { setDrawerOpen(true); }; @@ -575,81 +535,14 @@ const MessagesList = () => { [classes.mainWrapperShift]: drawerOpen, })} > - - + - - - ) : ( - - ) - } - title={ - loading ? ( - - ) : ( - `${contact.name} #${ticket.id}` - ) - } - subheader={ - loading ? ( - - ) : ticket.user ? ( - `${i18n.t("messagesList.header.assignedTo")} ${ - ticket.user.name - }` - ) : ( - "Pending" - ) - } /> - {!loading && ( -
- {ticket.status === "closed" ? ( - - ) : ( - <> - - - - )} - - - - -
- )} -
+ +
{ ) : null}
-
); }; -export default MessagesList; +export default Ticket; diff --git a/frontend/src/components/TicketActionButtons/index.js b/frontend/src/components/TicketActionButtons/index.js new file mode 100644 index 0000000..2a0c774 --- /dev/null +++ b/frontend/src/components/TicketActionButtons/index.js @@ -0,0 +1,113 @@ +import React, { useState } from "react"; +import { useHistory } from "react-router-dom"; +import { toast } from "react-toastify"; + +import { makeStyles } from "@material-ui/core/styles"; +import { Button, IconButton } from "@material-ui/core"; +import { MoreVert, Replay } from "@material-ui/icons"; + +import { i18n } from "../../translate/i18n"; +import api from "../../services/api"; +import TicketOptionsMenu from "../TicketOptionsMenu"; + +const useStyles = makeStyles(theme => ({ + actionButtons: { + marginRight: 6, + flex: "none", + alignSelf: "center", + marginLeft: "auto", + "& > *": { + margin: theme.spacing(1), + }, + }, +})); + +const TicketActionButtons = ({ ticket }) => { + const classes = useStyles(); + const history = useHistory(); + const userId = +localStorage.getItem("userId"); + const [anchorEl, setAnchorEl] = useState(null); + const ticketOptionsMenuOpen = Boolean(anchorEl); + + const handleOpenTicketOptionsMenu = e => { + setAnchorEl(e.currentTarget); + }; + + const handleCloseTicketOptionsMenu = e => { + setAnchorEl(null); + }; + + const handleUpdateTicketStatus = async (e, status, userId) => { + try { + await api.put(`/tickets/${ticket.id}`, { + status: status, + userId: userId || null, + }); + + if (status === "open") { + history.push(`/tickets/${ticket.id}`); + } else { + history.push("/tickets"); + } + } catch (err) { + console.log(err); + if (err.response && err.response.data && err.response.data.error) { + toast.error(err.response.data.error); + } + } + }; + + return ( +
+ {ticket.status === "closed" && ( + + )} + {ticket.status === "open" && ( + <> + + + + + + + + )} + {ticket.status === "pending" && ( + + )} +
+ ); +}; + +export default TicketActionButtons; diff --git a/frontend/src/components/TicketHeader/index.js b/frontend/src/components/TicketHeader/index.js new file mode 100644 index 0000000..8725042 --- /dev/null +++ b/frontend/src/components/TicketHeader/index.js @@ -0,0 +1,28 @@ +import React from "react"; + +import { Card } from "@material-ui/core"; +import { makeStyles } from "@material-ui/core/styles"; +import TicketHeaderSkeleton from "../TicketHeaderSkeleton"; + +const useStyles = makeStyles(theme => ({ + ticketHeader: { + display: "flex", + backgroundColor: "#eee", + flex: "none", + borderBottom: "1px solid rgba(0, 0, 0, 0.12)", + }, +})); + +const TicketHeader = ({ loading, children }) => { + const classes = useStyles(); + + if (loading) return ; + + return ( + + {children} + + ); +}; + +export default TicketHeader; diff --git a/frontend/src/components/TicketHeader/old.js b/frontend/src/components/TicketHeader/old.js new file mode 100644 index 0000000..0cbdb0f --- /dev/null +++ b/frontend/src/components/TicketHeader/old.js @@ -0,0 +1,272 @@ +import React from "react"; + +import { + Avatar, + Button, + Card, + CardHeader, + IconButton, +} from "@material-ui/core"; +import { makeStyles } from "@material-ui/core/styles"; +import { green } from "@material-ui/core/colors"; +import Skeleton from "@material-ui/lab/Skeleton"; +import { Replay } from "@material-ui/icons"; + +import TicketOptionsMenu from "../TicketOptionsMenu"; +import { i18n } from "../../translate/i18n"; + +const drawerWidth = 320; + +const useStyles = makeStyles(theme => ({ + root: { + display: "flex", + height: "100%", + position: "relative", + overflow: "hidden", + }, + + 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, + }, + + messagesHeader: { + display: "flex", + // cursor: "pointer", + backgroundColor: "#eee", + flex: "none", + borderBottom: "1px solid rgba(0, 0, 0, 0.12)", + }, + + actionButtons: { + marginRight: 6, + flex: "none", + alignSelf: "center", + marginLeft: "auto", + "& > *": { + margin: theme.spacing(1), + }, + }, + + messagesListWrapper: { + overflow: "hidden", + position: "relative", + display: "flex", + flexDirection: "column", + flexGrow: 1, + }, + + circleLoading: { + color: green[500], + position: "absolute", + opacity: "70%", + top: 0, + left: "50%", + marginTop: 12, + }, + + messageLeft: { + marginRight: 20, + marginTop: 2, + minWidth: 100, + maxWidth: 600, + height: "auto", + display: "block", + position: "relative", + + whiteSpace: "pre-wrap", + backgroundColor: "#ffffff", + color: "#303030", + alignSelf: "flex-start", + borderTopLeftRadius: 0, + borderTopRightRadius: 8, + borderBottomLeftRadius: 8, + borderBottomRightRadius: 8, + paddingLeft: 5, + paddingRight: 5, + paddingTop: 5, + paddingBottom: 0, + boxShadow: "0 1px 1px #b3b3b3", + }, + + messageRight: { + marginLeft: 20, + marginTop: 2, + minWidth: 100, + maxWidth: 600, + height: "auto", + display: "block", + position: "relative", + + whiteSpace: "pre-wrap", + backgroundColor: "#dcf8c6", + color: "#303030", + alignSelf: "flex-end", + borderTopLeftRadius: 8, + borderTopRightRadius: 8, + borderBottomLeftRadius: 8, + borderBottomRightRadius: 0, + paddingLeft: 5, + paddingRight: 5, + paddingTop: 5, + paddingBottom: 0, + boxShadow: "0 1px 1px #b3b3b3", + }, + + textContentItem: { + overflowWrap: "break-word", + padding: "3px 80px 6px 6px", + }, + + messageMedia: { + objectFit: "cover", + width: 250, + height: 200, + borderTopLeftRadius: 8, + borderTopRightRadius: 8, + borderBottomLeftRadius: 8, + borderBottomRightRadius: 8, + }, + + timestamp: { + fontSize: 11, + position: "absolute", + bottom: 0, + right: 5, + color: "#999", + }, + + dailyTimestamp: { + alignItems: "center", + textAlign: "center", + alignSelf: "center", + width: "110px", + backgroundColor: "#e1f3fb", + margin: "10px", + borderRadius: "10px", + boxShadow: "0 1px 1px #b3b3b3", + }, + + dailyTimestampText: { + color: "#808888", + padding: 8, + alignSelf: "center", + marginLeft: "0px", + }, + + ackIcons: { + fontSize: 18, + verticalAlign: "middle", + marginLeft: 4, + }, + + ackDoneAllIcon: { + color: green[500], + fontSize: 18, + verticalAlign: "middle", + marginLeft: 4, + }, +})); + +const TicketHeader = ({ loading, contact, ticket }) => { + const classes = useStyles(); + + return ( + + + + + ) : ( + + ) + } + title={ + loading ? ( + + ) : ( + `${contact.name} #${ticket.id}` + ) + } + subheader={ + loading ? ( + + ) : ticket.user ? ( + `${i18n.t("messagesList.header.assignedTo")} ${ticket.user.name}` + ) : ( + "Pending" + ) + } + /> + {!loading && ( +
+ {ticket.status === "closed" ? ( + + ) : ( + <> + + + + )} + {/* + + + */} +
+ )} +
+ ); +}; + +export default TicketHeader; diff --git a/frontend/src/components/TicketHeaderSkeleton/index.js b/frontend/src/components/TicketHeaderSkeleton/index.js new file mode 100644 index 0000000..1bd583c --- /dev/null +++ b/frontend/src/components/TicketHeaderSkeleton/index.js @@ -0,0 +1,36 @@ +import React from "react"; + +import { makeStyles } from "@material-ui/core/styles"; +import { Avatar, Card, CardHeader } from "@material-ui/core"; +import Skeleton from "@material-ui/lab/Skeleton"; + +const useStyles = makeStyles(theme => ({ + ticketHeader: { + display: "flex", + backgroundColor: "#eee", + flex: "none", + borderBottom: "1px solid rgba(0, 0, 0, 0.12)", + }, +})); + +const TicketHeaderSkeleton = () => { + const classes = useStyles(); + + return ( + + + + + } + title={} + subheader={} + /> + + ); +}; + +export default TicketHeaderSkeleton; diff --git a/frontend/src/components/TicketInfo/index.js b/frontend/src/components/TicketInfo/index.js new file mode 100644 index 0000000..914e261 --- /dev/null +++ b/frontend/src/components/TicketInfo/index.js @@ -0,0 +1,24 @@ +import React from "react"; + +import { Avatar, CardHeader } from "@material-ui/core"; + +import { i18n } from "../../translate/i18n"; + +const TicketInfo = ({ contact, ticket, onClick }) => { + return ( + } + title={`${contact.name} #${ticket.id}`} + subheader={ + ticket.user && + `${i18n.t("messagesList.header.assignedTo")} ${ticket.user.name}` + } + /> + ); +}; + +export default TicketInfo; diff --git a/frontend/src/components/TicketsList/index.js b/frontend/src/components/TicketsList/index.js index 0aa075a..db4d845 100644 --- a/frontend/src/components/TicketsList/index.js +++ b/frontend/src/components/TicketsList/index.js @@ -6,7 +6,7 @@ import List from "@material-ui/core/List"; import Paper from "@material-ui/core/Paper"; import TicketListItem from "../TicketListItem"; -import TicketsSkeleton from "../TicketsSkeleton"; +import TicketsListSkeleton from "../TicketsListSkeleton"; import useTickets from "../../hooks/useTickets"; import { i18n } from "../../translate/i18n"; @@ -276,7 +276,7 @@ const TicketsList = ({ status, searchParam, showAll }) => { ))} )} - {loading && } + {loading && } diff --git a/frontend/src/components/TicketsSkeleton/index.js b/frontend/src/components/TicketsListSkeleton/index.js similarity index 100% rename from frontend/src/components/TicketsSkeleton/index.js rename to frontend/src/components/TicketsListSkeleton/index.js diff --git a/frontend/src/components/Tickets/index.js b/frontend/src/components/TicketsManager/index.js similarity index 94% rename from frontend/src/components/Tickets/index.js rename to frontend/src/components/TicketsManager/index.js index 18236a2..31286fe 100644 --- a/frontend/src/components/Tickets/index.js +++ b/frontend/src/components/TicketsManager/index.js @@ -81,7 +81,7 @@ const useStyles = makeStyles(theme => ({ }, })); -const Tickets = () => { +const TicketsManager = () => { const classes = useStyles(); const [searchParam, setSearchParam] = useState(""); @@ -186,4 +186,4 @@ const Tickets = () => { ); }; -export default Tickets; +export default TicketsManager; diff --git a/frontend/src/pages/Tickets/index.js b/frontend/src/pages/Tickets/index.js index bc77923..9e6ea20 100644 --- a/frontend/src/pages/Tickets/index.js +++ b/frontend/src/pages/Tickets/index.js @@ -4,8 +4,8 @@ import Grid from "@material-ui/core/Grid"; import Paper from "@material-ui/core/Paper"; import { makeStyles } from "@material-ui/core/styles"; -import Tickets from "../../components/Tickets/"; -import MessagesList from "../../components/MessagesList/"; +import TicketsManager from "../../components/TicketsManager/"; +import Ticket from "../../components/Ticket/"; import { i18n } from "../../translate/i18n"; @@ -54,12 +54,12 @@ const Chat = () => {
- + {ticketId ? ( <> - + ) : (