projeto-hit/frontend/src/components/TicketsManager/index.js

564 lines
16 KiB
JavaScript

import React, { useContext, useEffect, useRef, useState } from "react"
import { makeStyles } from "@material-ui/core/styles"
import { IconButton } from "@mui/material"
import Paper from "@material-ui/core/Paper"
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 Tooltip from "@material-ui/core/Tooltip"
import SearchIcon from "@material-ui/icons/Search"
import MoveToInboxIcon from "@material-ui/icons/MoveToInbox"
import CheckBoxIcon from "@material-ui/icons/CheckBox"
import MenuIcon from "@material-ui/icons/Menu"
import FindInPageIcon from '@material-ui/icons/FindInPage'
import FormControlLabel from "@material-ui/core/FormControlLabel"
import Switch from "@material-ui/core/Switch"
import openSocket from "socket.io-client"
import NewTicketModal from "../NewTicketModal"
import TicketsList from "../TicketsList"
import TabPanel from "../TabPanel"
import { i18n } from "../../translate/i18n"
import { AuthContext } from "../../context/Auth/AuthContext"
import { Can } from "../Can"
import TicketsQueueSelect from "../TicketsQueueSelect"
import { Button } from "@material-ui/core"
import { TabTicketContext } from "../../context/TabTicketHeaderOption/TabTicketHeaderOption"
import { SearchTicketContext } from "../../context/SearchTicket/SearchTicket"
import useTickets from "../../hooks/useTickets"
import api from "../../services/api"
import toastError from "../../errors/toastError"
import { ticketsContext } from "../../context/TicketsProvider/TicketsProvider"
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,
display: "flex",
flexDirection: "column",
gap: "10px",
borderRadius: 40,
padding: 4,
marginRight: theme.spacing(1),
},
searchInputHeader: {
flex: 1,
background: "#fff",
display: "flex",
borderRadius: 40,
padding: 4,
marginRight: theme.spacing(1),
},
searchContentInput: {
flex: 1,
background: "#fff",
display: "flex",
borderRadius: 40,
padding: 4,
marginRight: theme.spacing(1),
},
searchIcon: {
color: "grey",
marginLeft: 6,
marginRight: 6,
alignSelf: "center",
},
menuSearch: {
color: "grey",
alignSelf: "center",
},
searchInput: {
flex: 1,
border: "none",
borderRadius: 30,
},
badge: {
right: "-10px",
},
show: {
display: "block",
},
hide: {
display: "none !important",
},
}))
const DEFAULT_SEARCH_PARAM = { searchParam: "", searchParamContent: "" }
const TicketsManager = () => {
const { tabOption, setTabOption } = useContext(TabTicketContext)
const { setSearchTicket } = useContext(SearchTicketContext)
const classes = useStyles()
const [searchParam, setSearchParam] = useState(DEFAULT_SEARCH_PARAM)
const [tab, setTab] = useState("open")
const [tabOpen, setTabOpen] = useState("open")
const [newTicketModalOpen, setNewTicketModalOpen] = useState(false)
const [showAllTickets, setShowAllTickets] = useState(false)
const { user, setting, getSettingValue } = 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 [showContentSearch, setShowContentSearch] = useState(false)
const searchInputRef = useRef()
const searchContentInputRef = useRef()
const [inputSearch, setInputSearch] = useState('')
const [inputContentSearch, setInputContentSearch] = useState("")
const [openTooltipSearch, setOpenTooltipSearch] = useState(false)
const [waitingTime, setWaitingTime] = useState('00:00')
// const [tickets, setTickets] = useState([]);
const [settings, setSettings] = useState([])
let searchTimeout
let searchContentTimeout
const { tickets, } = useContext(ticketsContext)
useEffect(() => {
setSettings(setting)
}, [setting])
useEffect(() => {
if (user.profile.toUpperCase() === "ADMIN" ||
user.profile.toUpperCase() === "SUPERVISOR" ||
user.profile.toUpperCase() === "MASTER") {
setShowAllTickets(true)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useEffect(() => {
if (tab === "search") {
searchInputRef.current.focus()
}
setTabOption(tab)
}, [tab, setTabOption])
useEffect(() => {
if (settings?.length > 0 && getSettingValue('waitingTimeTickets') !== 'enabled') return
const calculateAverageTime = () => {
if (tickets.length > 0) {
const now = new Date()
const differenceTime = tickets?.map(ticket => {
const createdAt = new Date(ticket.createdAt)
const difference = now - createdAt
return difference
})
const sumDifferences = differenceTime.reduce((total, difference) => total + difference, 0)
const averageTimeMilliseconds = sumDifferences / tickets?.length
let hours = Math.floor(averageTimeMilliseconds / 3600000)
const minutes = Math.floor((averageTimeMilliseconds % 3600000) / 60000)
let days = hours >= 24 ? parseInt(hours / 24) : ''
if (days != '') hours = hours - (24 * days)
const averageTimeFormated = `${days != '' ? `${days}d ` : days}${hours.toString().padStart(2, '0')}h${minutes.toString().padStart(2, '0')}`
return averageTimeFormated
} else return '00:00'
}
setWaitingTime(calculateAverageTime())
const intervalId = setInterval(() => {
setWaitingTime(calculateAverageTime())
}, 10000)
return () => clearInterval(intervalId)
}, [tickets])
useEffect(() => {
// clearTimeout(searchContentTimeout);
// setSearchParam(prev => ({ ...prev, searchParamContent: "" }))
if (!inputContentSearch) return
if (!searchContentTimeout) return
// searchContentTimeout = setTimeout(() => {
// setSearchParam(prev => ({ ...prev, searchParamContent: inputContentSearch }));
// }, 500);
clearTimeout(searchContentTimeout)
setSearchParam(prev => ({ ...prev, searchParamContent: "" }))
}, [inputContentSearch, searchContentTimeout])
useEffect(() => {
//console.log(selectedQueueIds);
if (tabOption === 'open') {
setTabOption('')
setSearchParam(DEFAULT_SEARCH_PARAM)
setInputSearch('')
setInputContentSearch('')
setTab("open")
return
}
}, [tabOption, setTabOption])
const removeExtraSpace = (str) => {
str = str.replace(/^\s+/g, '')
return str.replace(/\s+/g, ' ')
}
const handleSearch = (e) => {
let searchedTerm = e.target.value.toLowerCase()
setInputSearch(removeExtraSpace(searchedTerm))
setSearchTicket(searchParam.searchParam)
clearTimeout(searchTimeout)
if (searchedTerm === "") {
setSearchParam(prev => ({ ...prev, searchParam: searchedTerm }))
setInputSearch(searchedTerm)
setShowContentSearch(false)
setTab("open")
return
}
if (searchedTerm.length < 4) {
setSearchParam(prev => ({ ...prev, searchParamContent: "" }))
setInputContentSearch('')
}
searchTimeout = setTimeout(() => {
setSearchParam(prev => ({ ...prev, searchParam: searchedTerm }))
}, 500)
}
const handleContentSearch = e => {
let searchedContentText = removeExtraSpace(e.target.value.toLowerCase())
setInputContentSearch(searchedContentText)
searchContentTimeout = setTimeout(() => {
setSearchParam(prev => ({ ...prev, searchParamContent: searchedContentText }))
}, 500)
}
const handleOpenTooltipSearch = () => {
if (searchParam.searchParam.length < 4) {
setOpenTooltipSearch(true)
}
}
const handleCloseTooltipSearch = () => {
setOpenTooltipSearch(false)
if (searchParam.searchParam.length < 4) {
searchInputRef.current.focus()
}
}
const handleChangeTab = (e, newValue) => {
setTab(newValue)
}
const handleChangeTabOpen = (e, newValue) => {
setTabOpen(newValue)
}
const applyPanelStyle = (status) => {
if (tabOpen !== status) {
return { width: 0, height: 0 }
}
}
return (
<Paper elevation={0} variant="outlined" className={classes.ticketsWrapper}>
<NewTicketModal
modalOpen={newTicketModalOpen}
onClose={(e) => setNewTicketModalOpen(false)}
/>
<Paper elevation={0} square className={classes.tabsHeader}>
<Tabs
value={tab}
onChange={handleChangeTab}
variant="fullWidth"
indicatorColor="primary"
textColor="primary"
aria-label="icon label tabs example"
>
<Tab
value={"open"}
icon={<MoveToInboxIcon />}
label={i18n.t("tickets.tabs.open.title")}
classes={{ root: classes.tab }}
/>
<Tab
value={"closed"}
icon={<CheckBoxIcon />}
label={i18n.t("tickets.tabs.closed.title")}
classes={{ root: classes.tab }}
/>
<Tab
value={"search"}
icon={<SearchIcon />}
label={i18n.t("tickets.tabs.search.title")}
classes={{ root: classes.tab }}
/>
</Tabs>
</Paper>
<Paper square elevation={0} className={classes.ticketOptionsBox}>
{tab === "search" ? (
<div className={classes.serachInputWrapper}>
<div className={classes.searchInputHeader}>
<SearchIcon className={classes.searchIcon} />
<InputBase
className={classes.searchInput}
inputRef={searchInputRef}
placeholder={i18n.t("tickets.search.placeholder")}
type="search"
value={inputSearch}
onChange={handleSearch}
/>
{/* <IconButton onClick={() => setShowContentSearch(prev => !prev)}>
<MenuIcon className={classes.menuSearch} />
</IconButton> */}
{/* <Tooltip
open={openTooltipSearch}
onOpen={() => handleOpenTooltipSearch()}
onClose={() => handleCloseTooltipSearch()}
title="Digite pelo menos 4 caracteres"
arrow>
<span>
<IconButton
disabled={searchParam.searchParam.length < 4}
onClick={() => setShowContentSearch(prev => !prev)}
>
<MenuIcon className={classes.menuSearch} />
</IconButton>
</span>
</Tooltip> */}
</div>
{
// showContentSearch ?
// (showContentSearch /*&& searchParam.searchParam.length >= 4*/) ?
// (<div className={classes.searchContentInput}>
// <FindInPageIcon className={classes.searchIcon} />
// <InputBase
// className={classes.searchInput}
// inputRef={searchContentInputRef}
// placeholder={i18n.t("Busca por conteúdo")}
// type="search"
// value={inputContentSearch}
// onChange={(e) => handleContentSearch(e)}
// />
// </div>) : null
}
</div>
) : (
<>
<Button
variant="outlined"
color="primary"
onClick={() => setNewTicketModalOpen(true)}
>
{i18n.t("ticketsManager.buttons.newTicket")}
</Button>
<Can
role={user.profile}
perform="tickets-manager:showall"
yes={() => (
<FormControlLabel
label={i18n.t("tickets.buttons.showAll")}
labelPlacement="start"
control={
<Switch
size="small"
checked={showAllTickets}
onChange={() =>
setShowAllTickets((prevState) => !prevState)
}
name="showAllTickets"
color="primary"
/>
}
/>
)}
/>
</>
)}
<TicketsQueueSelect
style={{ marginLeft: 6 }}
selectedQueueIds={selectedQueueIds}
userQueues={user?.queues}
onChange={(values) => setSelectedQueueIds(values)}
/>
</Paper>
<TabPanel value={tab} name="open" className={classes.ticketsWrapper}>
<Tabs
value={tabOpen}
onChange={handleChangeTabOpen}
indicatorColor="primary"
textColor="primary"
variant="fullWidth"
>
<Tab
label={
<Badge
className={classes.badge}
badgeContent={openCount}
color="primary"
>
{i18n.t("ticketsList.assignedHeader")}
</Badge>
}
value={"open"}
/>
<Tab
label={
<Badge
className={classes.badge}
badgeContent={pendingCount}
color="secondary"
>
{i18n.t("ticketsList.pendingHeader")}
</Badge>
}
value={"pending"}
/>{
(settings?.length > 0 && getSettingValue('waitingTimeTickets') === 'enabled') &&
<Tooltip
arrow
placement="right"
title={"Tempo de espera aguardando"}
>
<span style={{ display: 'flex', alignItems: 'center', flexDirection: 'column', justifyContent: 'flex-start', marginRight: '20px', marginTop: '10px' }}>
{/* <label style={{ color: 'red', fontWeight: 'bold', padding: '.1rem', fontSize: '8px', textAlign: 'center', margin: '0' }}>
<i>ESPERA</i>
</label> */}
<label style={{ color: 'gray', fontWeight: 'bold', padding: '5px'/*, textDecoration: 'underline'*/, fontSize: '13px' }}>
{waitingTime}
</label>
</span>
</Tooltip>
}
</Tabs>
<Paper className={classes.ticketsWrapper}>
<TicketsList
status={"open"}
showAll={showAllTickets}
selectedQueueIds={selectedQueueIds}
updateCount={(val) => setOpenCount(val)}
style={applyPanelStyle("open")}
/>
<TicketsList
status="pending"
selectedQueueIds={selectedQueueIds}
updateCount={(val) => setPendingCount(val)}
style={applyPanelStyle("pending")}
/>
</Paper>
</TabPanel>
<TabPanel value={tab} name="closed" className={classes.ticketsWrapper}>
<TicketsList
status="closed"
showAll={true}
selectedQueueIds={selectedQueueIds}
/>
</TabPanel>
<TabPanel value={tab} name="search" className={classes.ticketsWrapper}>
<TicketsList
searchParam={searchParam.searchParam}
searchParamContent={searchParam.searchParamContent}
tab={tab}
showAll={true}
selectedQueueIds={selectedQueueIds}
/>
</TabPanel>
</Paper>
)
}
export default TicketsManager