updante do modulo campanha em andamento

pull/21/head
adriano 2023-08-04 16:16:19 -03:00
parent 1e0b8b9d29
commit ff0d2c3630
5 changed files with 1316 additions and 232 deletions

View File

@ -0,0 +1,346 @@
import React, { useState, useEffect, useContext, createContext } from 'react'
import * as Yup from 'yup'
import { Formik, Form, Field } from 'formik'
import { toast } from 'react-toastify'
import { makeStyles } from '@material-ui/core/styles'
import { green } from '@material-ui/core/colors'
import { AuthContext } from '../../context/Auth/AuthContext'
import { Can } from '../../components/Can'
import apiBroker from '../../services/apiBroker'
import Select from "@material-ui/core/Select"
import MenuItem from "@material-ui/core/MenuItem";
import SelectField from "../../components/Report/SelectField"
import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext";
import {
Dialog,
DialogContent,
DialogTitle,
Button,
DialogActions,
CircularProgress,
TextField,
Switch,
FormControlLabel,
} from '@material-ui/core'
import api from '../../services/api'
import { i18n } from '../../translate/i18n'
import toastError from '../../errors/toastError'
import QueueSelect from '../QueueSelect'
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
flexWrap: 'wrap',
},
multFieldLine: {
display: 'flex',
'& > *:not(:last-child)': {
marginRight: theme.spacing(1),
},
},
btnWrapper: {
position: 'relative',
},
buttonProgress: {
color: green[500],
position: 'absolute',
top: '50%',
left: '50%',
marginTop: -12,
marginLeft: -12,
},
}))
const SessionSchema = Yup.object().shape({
name: Yup.string()
.min(2, 'Too Short!')
.max(100, 'Too Long!')
.required('Required'),
})
const CampaignModal = ({ open, onClose, campaignId, dispatch }) => {
const classes = useStyles()
const initialState = {
name: '',
message: '',
whatsapp_sender: '',
csv_original_file_name: '',
}
const { user } = useContext(AuthContext)
const { whatsApps } = useContext(WhatsAppsContext);
console.log('------------> whatsApps: ', whatsApps)
const [campaign, setCampaign] = useState(initialState)
// const [selectedQueueIds, setSelectedQueueIds] = useState([])
const [file, setFile] = useState()
const [selectedNumber, setSelectedNumber] = useState('');
const [itemHover, setItemHover] = useState(-1)
useEffect(() => {
console.log('selectedNumber: ', selectedNumber)
}, [selectedNumber])
useEffect(() => {
const fetchSession = async () => {
if (!campaignId) return
try {
const { data } = await apiBroker.get('/campaign', {
params: {
adminId: user.id,
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
identifier: 'campaign',
id: campaignId
}
})
setCampaign(data.campaign)
setSelectedNumber(data.campaign.whatsapp_sender)
} catch (err) {
toastError(err)
}
}
fetchSession()
}, [campaignId])
const handleSaveCampaign = async (values) => {
let response = null
const formData = new FormData()
formData.append('adminId', user.id)
formData.append('baseURL', process.env.REACT_APP_BACKEND_URL_PRIVATE)
formData.append('frontURL', process.env.REACT_APP_FRONTEND_URL)
formData.append('identifier', 'campaign')
formData.append('file', file)
formData.append('name', values.name)
formData.append('whatsapp_sender', selectedNumber)
formData.append('message', values.message)
formData.append('csv_original_file_name', file?.name)
const config = {
headers: {
'content-type': 'multipart/form-data',
},
}
try {
if (campaignId) {
response = await apiBroker.patch(`/campaign/${campaignId}`,
formData,
config
)
toast.success('Campanha atualizada com sucesso!')
} else {
response = await apiBroker.post('/campaign',
formData,
config
)
toast.success('Campanha criada com sucesso!')
}
dispatch({ type: "UPDATE_CAMPAIGNS", payload: response.data.campaign })
handleClose()
} catch (err) {
toastError(err)
}
}
const handleClose = () => {
onClose()
setCampaign(initialState)
setSelectedNumber('')
}
async function handleChange(event) {
try {
if (event.target.files[0].size > 1024 * 1024 * 4) {
alert('Arquivo não pode ser maior que 4 MB!')
return
}
console.log('event.target.files[0]: ', event.target.files[0])
setFile(event.target.files[0])
} catch (err) {
toastError(err)
}
}
return (
<div className={classes.root}>
<Dialog
open={open}
onClose={handleClose}
maxWidth="sm"
fullWidth
scroll="paper"
>
<DialogTitle>
{campaignId ? 'Editar campanha' : 'Adicionar campanha'}
</DialogTitle>
<Formik
initialValues={campaign}
enableReinitialize={true}
validationSchema={SessionSchema}
onSubmit={(values, actions) => {
setTimeout(() => {
handleSaveCampaign(values)
actions.setSubmitting(false)
}, 400)
}}
>
{({ values, touched, errors, isSubmitting }) => (
<Form>
<DialogContent dividers>
<Can
role={user.profile}
perform="url-remote-session:show"
yes={() => (
<>
<div>
<div className={classes.multFieldLine}>
<Field
as={TextField}
label={i18n.t('whatsappModal.form.name')}
autoFocus
name="name"
error={touched.name && Boolean(errors.name)}
helperText={touched.name && errors.name}
variant="outlined"
margin="dense"
className={classes.textField}
/>
<Select
value={selectedNumber}
onChange={(e) => setSelectedNumber(e.target.value)}
label={i18n.t("transferTicketModal.fieldQueuePlaceholder")}
required
>
<MenuItem style={{ background: "white", }} value={''}>&nbsp;</MenuItem>
{whatsApps.map((whatsapp) => (
<MenuItem
key={whatsapp.id}
value={whatsapp.number}
onMouseEnter={() => setItemHover(whatsapp.id)}
onMouseLeave={() => setItemHover(-1)}
>{whatsapp.number}
</MenuItem>
))}
</Select>
</div>
<div className={classes.multFieldLine}>
</div>
</div>
</>
)}
/>
<div>
<Field
as={TextField}
label="Mensagem"
type="message"
multiline
rows={5}
fullWidth
name="message"
error={touched.message && Boolean(errors.message)}
helperText={touched.message && errors.message}
variant="outlined"
margin="dense"
/>
</div>
<div
style={{
display: 'flex',
gap: '10px',
}}
>
<input
type="file"
accept=".csv"
style={{ display: 'none' }}
onChange={handleChange}
id="contained-button-file"
/>
<label htmlFor="contained-button-file">
<Button
variant="contained"
color="primary"
component="span"
>
CSV UPLOAD
</Button>
</label>
<h3>{file?.name || campaign?.csv_original_file_name}</h3>
</div>
</DialogContent>
<DialogActions>
<Button
onClick={handleClose}
color="secondary"
disabled={isSubmitting}
variant="outlined"
>
{i18n.t('whatsappModal.buttons.cancel')}
</Button>
<Button
type="submit"
color="primary"
disabled={isSubmitting}
variant="contained"
className={classes.btnWrapper}
>
{campaignId
? i18n.t('whatsappModal.buttons.okEdit')
: i18n.t('whatsappModal.buttons.okAdd')}
{isSubmitting && (
<CircularProgress
size={24}
className={classes.buttonProgress}
/>
)}
</Button>
</DialogActions>
</Form>
)}
</Formik>
</Dialog>
</div>
)
}
export default React.memo(CampaignModal)

View File

@ -1,40 +1,42 @@
import React, { useContext, useEffect, useState } from "react"; import React, { useContext, useEffect, useState } from 'react'
import { Link as RouterLink } from "react-router-dom"; import { Link as RouterLink } from 'react-router-dom'
import ListItem from "@material-ui/core/ListItem"; import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from "@material-ui/core/ListItemIcon"; import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemText from "@material-ui/core/ListItemText"; import ListItemText from '@material-ui/core/ListItemText'
import ListSubheader from "@material-ui/core/ListSubheader"; import ListSubheader from '@material-ui/core/ListSubheader'
import Divider from "@material-ui/core/Divider"; import Divider from '@material-ui/core/Divider'
import { Badge } from "@material-ui/core"; import { Badge } from '@material-ui/core'
import DashboardOutlinedIcon from "@material-ui/icons/DashboardOutlined"; import DashboardOutlinedIcon from '@material-ui/icons/DashboardOutlined'
import ReportOutlinedIcon from "@material-ui/icons/ReportOutlined"; import ReportOutlinedIcon from '@material-ui/icons/ReportOutlined'
import SendOutlined from "@material-ui/icons/SendOutlined"; import SendOutlined from '@material-ui/icons/SendOutlined'
//import ReportOutlined from "@bit/mui-org.material-ui-icons.report-outlined"; //import ReportOutlined from "@bit/mui-org.material-ui-icons.report-outlined";
import WhatsAppIcon from '@material-ui/icons/WhatsApp'
import SyncAltIcon from '@material-ui/icons/SyncAlt'
import SettingsOutlinedIcon from '@material-ui/icons/SettingsOutlined'
import PeopleAltOutlinedIcon from '@material-ui/icons/PeopleAltOutlined'
import ContactPhoneOutlinedIcon from '@material-ui/icons/ContactPhoneOutlined'
import AccountTreeOutlinedIcon from '@material-ui/icons/AccountTreeOutlined'
import QuestionAnswerOutlinedIcon from '@material-ui/icons/QuestionAnswerOutlined'
import WhatsAppIcon from "@material-ui/icons/WhatsApp"; import { i18n } from '../translate/i18n'
import SyncAltIcon from "@material-ui/icons/SyncAlt"; import { WhatsAppsContext } from '../context/WhatsApp/WhatsAppsContext'
import SettingsOutlinedIcon from "@material-ui/icons/SettingsOutlined"; import { AuthContext } from '../context/Auth/AuthContext'
import PeopleAltOutlinedIcon from "@material-ui/icons/PeopleAltOutlined"; import { Can } from '../components/Can'
import ContactPhoneOutlinedIcon from "@material-ui/icons/ContactPhoneOutlined";
import AccountTreeOutlinedIcon from "@material-ui/icons/AccountTreeOutlined";
import QuestionAnswerOutlinedIcon from "@material-ui/icons/QuestionAnswerOutlined";
import { i18n } from "../translate/i18n";
import { WhatsAppsContext } from "../context/WhatsApp/WhatsAppsContext";
import { AuthContext } from "../context/Auth/AuthContext";
import { Can } from "../components/Can";
function ListItemLink(props) { function ListItemLink(props) {
const { icon, primary, to, className } = props; const { icon, primary, to, className } = props
const renderLink = React.useMemo( const renderLink = React.useMemo(
() => React.forwardRef((itemProps, ref) => <RouterLink to={to} ref={ref} {...itemProps} />), () =>
React.forwardRef((itemProps, ref) => (
<RouterLink to={to} ref={ref} {...itemProps} />
)),
[to] [to]
); )
return ( return (
<li> <li>
@ -43,55 +45,59 @@ function ListItemLink(props) {
<ListItemText primary={primary} /> <ListItemText primary={primary} />
</ListItem> </ListItem>
</li> </li>
); )
} }
const MainListItems = (props) => { const MainListItems = (props) => {
const { setDrawerOpen } = props; const { setDrawerOpen } = props
const { whatsApps } = useContext(WhatsAppsContext); const { whatsApps } = useContext(WhatsAppsContext)
const { user } = useContext(AuthContext); const { user } = useContext(AuthContext)
const [connectionWarning, setConnectionWarning] = useState(false); const [connectionWarning, setConnectionWarning] = useState(false)
useEffect(() => { useEffect(() => {
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
if (whatsApps.length > 0) { if (whatsApps.length > 0) {
const offlineWhats = whatsApps.filter((whats) => { const offlineWhats = whatsApps.filter((whats) => {
return ( return (
whats.status === "qrcode" || whats.status === 'qrcode' ||
whats.status === "PAIRING" || whats.status === 'PAIRING' ||
whats.status === "DISCONNECTED" || whats.status === 'DISCONNECTED' ||
whats.status === "TIMEOUT" || whats.status === 'TIMEOUT' ||
whats.status === "OPENING" whats.status === 'OPENING'
); )
}); })
if (offlineWhats.length > 0) { if (offlineWhats.length > 0) {
setConnectionWarning(true); setConnectionWarning(true)
} else { } else {
setConnectionWarning(false); setConnectionWarning(false)
} }
} }
}, 2000); }, 2000)
return () => clearTimeout(delayDebounceFn); return () => clearTimeout(delayDebounceFn)
}, [whatsApps]); }, [whatsApps])
return ( return (
//Solicitado pelo Adriano: Click no LinkItem e fechar o menu! //Solicitado pelo Adriano: Click no LinkItem e fechar o menu!
<div onClick={() => setDrawerOpen(false)}> <div onClick={() => setDrawerOpen(false)}>
<ListItemLink <ListItemLink
to="/tickets" to="/tickets"
primary={i18n.t("mainDrawer.listItems.tickets")} primary={i18n.t('mainDrawer.listItems.tickets')}
icon={<WhatsAppIcon />} icon={<WhatsAppIcon />}
/> />
<ListItemLink <ListItemLink
to="/contacts" to="/contacts"
primary={i18n.t("mainDrawer.listItems.contacts")} primary={i18n.t('mainDrawer.listItems.contacts')}
icon={<ContactPhoneOutlinedIcon />} icon={<ContactPhoneOutlinedIcon />}
/> />
<ListItemLink to="/schedulesReminder" primary={i18n.t("mainDrawer.listItems.schedules")} icon={<SendOutlined />} /> <ListItemLink
to="/schedulesReminder"
primary={i18n.t('mainDrawer.listItems.schedules')}
icon={<SendOutlined />}
/>
<ListItemLink <ListItemLink
to="/quickAnswers" to="/quickAnswers"
primary={i18n.t("mainDrawer.listItems.quickAnswers")} primary={i18n.t('mainDrawer.listItems.quickAnswers')}
icon={<QuestionAnswerOutlinedIcon />} icon={<QuestionAnswerOutlinedIcon />}
/> />
<Can <Can
@ -100,31 +106,47 @@ const MainListItems = (props) => {
yes={() => ( yes={() => (
<> <>
<Divider /> <Divider />
<ListSubheader inset>{i18n.t("mainDrawer.listItems.administration")}</ListSubheader> <ListSubheader inset>
{i18n.t('mainDrawer.listItems.administration')}
</ListSubheader>
<ListItemLink <ListItemLink
to="/users" to="/users"
primary={i18n.t("mainDrawer.listItems.users")} primary={i18n.t('mainDrawer.listItems.users')}
icon={<PeopleAltOutlinedIcon />} icon={<PeopleAltOutlinedIcon />}
/> />
<ListItemLink <ListItemLink
to="/queues" to="/queues"
primary={i18n.t("mainDrawer.listItems.queues")} primary={i18n.t('mainDrawer.listItems.queues')}
icon={<AccountTreeOutlinedIcon />} icon={<AccountTreeOutlinedIcon />}
/> />
<ListItemLink <ListItemLink
to="/connections" to="/connections"
primary={i18n.t("mainDrawer.listItems.connections")} primary={i18n.t('mainDrawer.listItems.connections')}
icon={ icon={
<Badge badgeContent={connectionWarning ? "!" : 0} color="error"> <Badge badgeContent={connectionWarning ? '!' : 0} color="error">
<SyncAltIcon /> <SyncAltIcon />
</Badge> </Badge>
} }
/> />
<ListItemLink to="/" primary="Dashboard" icon={<DashboardOutlinedIcon />} /> <ListItemLink
to="/"
primary="Dashboard"
icon={<DashboardOutlinedIcon />}
/>
<ListItemLink to="/report" primary="Relatório" icon={<ReportOutlinedIcon />} /> <ListItemLink
to="/report"
primary="Relatório"
icon={<ReportOutlinedIcon />}
/>
<ListItemLink
to="/campaign"
primary="Campanha"
icon={<ReportOutlinedIcon />}
/>
<Can <Can
role={user.profile} role={user.profile}
@ -132,7 +154,7 @@ const MainListItems = (props) => {
yes={() => ( yes={() => (
<ListItemLink <ListItemLink
to="/settings" to="/settings"
primary={i18n.t("mainDrawer.listItems.settings")} primary={i18n.t('mainDrawer.listItems.settings')}
icon={<SettingsOutlinedIcon />} icon={<SettingsOutlinedIcon />}
/> />
)} )}
@ -141,7 +163,7 @@ const MainListItems = (props) => {
)} )}
/> />
</div> </div>
); )
}; }
export default MainListItems; export default MainListItems

View File

@ -0,0 +1,696 @@
import React, { useState, useCallback, useEffect, useReducer, useContext } from 'react'
import { toast } from 'react-toastify'
import { format, parseISO } from 'date-fns'
import openSocket from 'socket.io-client'
import { makeStyles } from '@material-ui/core/styles'
import { green } from '@material-ui/core/colors'
import {
Button,
TableBody,
TableRow,
TableCell,
IconButton,
Table,
TableHead,
Paper,
Tooltip,
Typography,
CircularProgress,
} from '@material-ui/core'
import {
Edit,
CheckCircle,
SignalCellularConnectedNoInternet2Bar,
SignalCellularConnectedNoInternet0Bar,
SignalCellular4Bar,
CropFree,
DeleteOutline,
// Restore
} from '@material-ui/icons'
import MainContainer from '../../components/MainContainer'
import MainHeader from '../../components/MainHeader'
import MainHeaderButtonsWrapper from '../../components/MainHeaderButtonsWrapper'
import Title from '../../components/Title'
import TableRowSkeleton from '../../components/TableRowSkeleton'
import api from '../../services/api'
import CampaignModal from '../../components/CampaignModal'
import ConfirmationModal from '../../components/ConfirmationModal'
import QrcodeModal from '../../components/QrcodeModal'
import { i18n } from '../../translate/i18n'
// import { WhatsAppsContext } from '../../context/WhatsApp/WhatsAppsContext'
import toastError from '../../errors/toastError'
//--------
import { AuthContext } from '../../context/Auth/AuthContext'
import { Can } from '../../components/Can'
import apiBroker from '../../services/apiBroker'
const reducer = (state, action) => {
if (action.type === "LOAD_CAMPAIGNS") {
const campaigns = action.payload
return [...state, ...campaigns]
}
if (action.type === "UPDATE_CAMPAIGNS") {
console.log('STATE: ', state)
const campaign = action.payload
const campaignIndex = state.findIndex((c) => c.id === campaign.id)
if (campaignIndex !== -1) {
state[campaignIndex] = campaign
return [...state]
} else {
return [campaign, ...state]
}
}
if (action.type === "DELETE_CAMPAIGN") {
const campaignId = action.payload
const campaignIndex = state.findIndex((c) => c.id === campaignId)
if (campaignIndex !== -1) {
state.splice(campaignIndex, 1)
}
return [...state]
}
if (action.type === "RESET") {
return []
}
}
const useStyles = makeStyles((theme) => ({
mainPaper: {
flex: 1,
padding: theme.spacing(1),
overflowY: 'scroll',
...theme.scrollbarStyles,
},
customTableCell: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
tooltip: {
backgroundColor: '#f5f5f9',
color: 'rgba(0, 0, 0, 0.87)',
fontSize: theme.typography.pxToRem(14),
border: '1px solid #dadde9',
maxWidth: 450,
},
tooltipPopper: {
textAlign: 'center',
},
buttonProgress: {
color: green[500],
},
}))
const CustomToolTip = ({ title, content, children }) => {
const classes = useStyles()
return (
<Tooltip
arrow
classes={{
tooltip: classes.tooltip,
popper: classes.tooltipPopper,
}}
title={
<React.Fragment>
<Typography gutterBottom color="inherit">
{title}
</Typography>
{content && <Typography>{content}</Typography>}
</React.Fragment>
}
>
{children}
</Tooltip>
)
}
const Campaign = () => {
//--------
const { user } = useContext(AuthContext)
const classes = useStyles()
// const { whatsApps } = useContext(WhatsAppsContext)
// console.log('------------> whatsApps: ', whatsApps)
// const { campaigns, loading } = useContext(WhatsAppsContext)
const [campaignModalOpen, setCampaignModalOpen] = useState(false)
const [qrModalOpen, setQrModalOpen] = useState(false)
const [selectedCampaign, setSelectedCampaign] = useState(null)
const [confirmModalOpen, setConfirmModalOpen] = useState(false)
const [diskSpaceInfo, setDiskSpaceInfo] = useState({})
const [disabled, setDisabled] = useState(true)
const [settings, setSettings] = useState([])
const [buttons, setClicks] = useState([])
const [campaigns, dispatch] = useReducer(reducer, [])
const [loading, setLoading] = useState(true)
const confirmationModalInitialState = {
action: '',
title: '',
message: '',
campaignId: '',
csv_original_file_name:'',
open: false,
}
const [confirmModalInfo, setConfirmModalInfo] = useState(
confirmationModalInitialState
)
useEffect(() => {
dispatch({ type: "RESET" })
}, [])
useEffect(() => {
setLoading(true)
const delayDebounceFn = setTimeout(() => {
const fetchContacts = async () => {
try {
console.log('process.env.REACT_APP_BACKEND_URL_PRIVATE: ', process.env.REACT_APP_BACKEND_URL_PRIVATE)
const { data } = await apiBroker.get('/campaign', {
params: {
adminId: user.id,
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
identifier: 'campaign'
}
})
dispatch({ type: "LOAD_CAMPAIGNS", payload: data.campaign })
setLoading(false)
} catch (err) {
toastError(err)
}
}
fetchContacts()
}, 500)
return () => clearTimeout(delayDebounceFn)
}, [])
useEffect(() => {
const fetchSession = async () => {
try {
// const test = await apiBroker.get('/contacts/status/insert/onqueue', {
// params: {
// adminId: user.id,
// baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
// identifier: 'campaign',
// },
// })
// console.log('-------------------> test: ', test)
// const { data } = await api.get('/settings')
// setSettings(data)
} catch (err) {
toastError(err)
}
}
fetchSession()
}, [])
const getSettingValue = (key) => {
const { value } = settings.find((s) => s.key === key)
return value
}
const handleStartWhatsAppSession = async (campaignId) => {
try {
await api.post(`/whatsappsession/${campaignId}`)
} catch (err) {
toastError(err)
}
}
// useEffect(() => {
// for (let i = 0; i < campaigns.length; i++) {
// if (buttons.includes(campaigns[i].id)) {
// campaigns[i].disabled = true
// }
// }
// }, [campaigns, buttons])
const handleRequestNewQrCode = async (campaignId) => {
try {
await api.put(`/whatsappsession/${campaignId}`)
} catch (err) {
toastError(err)
}
}
const handleOpenCampaignModal = () => {
setSelectedCampaign(null)
setCampaignModalOpen(true)
}
const handleCloseCampaignModal = useCallback(() => {
setCampaignModalOpen(false)
setSelectedCampaign(null)
}, [setSelectedCampaign, setCampaignModalOpen])
const handleOpenQrModal = (campaign) => {
setSelectedCampaign(campaign)
setQrModalOpen(true)
}
const handleCloseQrModal = useCallback(() => {
setSelectedCampaign(null)
setQrModalOpen(false)
}, [setQrModalOpen, setSelectedCampaign])
const handleEditCampaign = (campaign) => {
setSelectedCampaign(campaign)
setCampaignModalOpen(true)
}
const handleOpenConfirmationModal = (action, campaignId) => {
if (action === 'disconnect') {
setConfirmModalInfo({
action: action,
title: i18n.t('connections.confirmationModal.disconnectTitle'),
message: i18n.t('connections.confirmationModal.disconnectMessage'),
campaignId: campaignId,
})
}
if (action === 'delete') {
setConfirmModalInfo({
action: action,
title: i18n.t('connections.confirmationModal.deleteTitle'),
message: i18n.t('connections.confirmationModal.deleteMessage'),
campaignId: campaignId,
})
}
setConfirmModalOpen(true)
}
const handleSubmitConfirmationModal = async () => {
if (confirmModalInfo.action === 'disconnect') {
try {
await api.delete(`/whatsappsession/${confirmModalInfo.campaignId}`)
} catch (err) {
toastError(err)
}
}
if (confirmModalInfo.action === 'delete') {
try {
await apiBroker.delete(`/campaign/${confirmModalInfo.campaignId}`, {
params: {
adminId: user.id,
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
identifier: 'campaign',
},
})
dispatch({ type: "DELETE_CAMPAIGN", payload: confirmModalInfo.campaignId })
toast.success('Campanha deletada com sucesso')
} catch (err) {
toastError(err)
}
}
setConfirmModalInfo(confirmationModalInitialState)
}
const renderActionButtons = (campaign) => {
return (
<Can
role={user.profile}
perform="connection-button:show"
yes={() => (
<>
{campaign.status === 'qrcode' && (
<Button
size="small"
variant="contained"
color="primary"
onClick={() => handleOpenQrModal(campaign)}
>
{i18n.t('connections.buttons.qrcode')}
</Button>
)}
{campaign.status === 'DISCONNECTED' && (
<>
<Button
size="small"
variant="outlined"
color="primary"
onClick={() => handleStartWhatsAppSession(campaign.id)}
>
{i18n.t('connections.buttons.tryAgain')}
</Button>{' '}
<Button
size="small"
variant="outlined"
color="secondary"
onClick={() => handleRequestNewQrCode(campaign.id)}
>
{i18n.t('connections.buttons.newQr')}
</Button>
</>
)}
{(campaign.status === 'CONNECTED' ||
campaign.status === 'PAIRING' ||
campaign.status === 'TIMEOUT') && (
<Button
size="small"
variant="outlined"
color="secondary"
onClick={() => {
handleOpenConfirmationModal('disconnect', campaign.id)
}}
>
{i18n.t('connections.buttons.disconnect')}
</Button>
)}
{campaign.status === 'OPENING' && (
<Button size="small" variant="outlined" disabled color="default">
{i18n.t('connections.buttons.connecting')}
</Button>
)}
</>
)}
/>
)
}
const renderStatusToolTips = (campaign) => {
return (
<div className={classes.customTableCell}>
{campaign.status === 'DISCONNECTED' && (
<CustomToolTip
title={i18n.t('connections.toolTips.disconnected.title')}
content={i18n.t('connections.toolTips.disconnected.content')}
>
<SignalCellularConnectedNoInternet0Bar color="secondary" />
</CustomToolTip>
)}
{campaign.status === 'OPENING' && (
<CircularProgress size={24} className={classes.buttonProgress} />
)}
{campaign.status === 'qrcode' && (
<CustomToolTip
title={i18n.t('connections.toolTips.qrcode.title')}
content={i18n.t('connections.toolTips.qrcode.content')}
>
<CropFree />
</CustomToolTip>
)}
{campaign.status === 'CONNECTED' && (
<CustomToolTip title={i18n.t('connections.toolTips.connected.title')}>
<SignalCellular4Bar style={{ color: green[500] }} />
</CustomToolTip>
)}
{(campaign.status === 'TIMEOUT' || campaign.status === 'PAIRING') && (
<CustomToolTip
title={i18n.t('connections.toolTips.timeout.title')}
content={i18n.t('connections.toolTips.timeout.content')}
>
<SignalCellularConnectedNoInternet2Bar color="secondary" />
</CustomToolTip>
)}
{/* {campaign.status === "RESTORING" && (
<CustomToolTip
title={i18n.t("connections.toolTips.disconnected.title")}
content={i18n.t("connections.toolTips.disconnected.content")}
>
<Restore color="secondary" />
</CustomToolTip>
)} */}
</div>
)
}
useEffect(() => {
const delayDebounceFn = setTimeout(() => {
const fetchQueries = async () => {
try {
await api.post(`/restartwhatsappsession/0`, {
params: { status: 'status' },
})
setDisabled(false)
setClicks((buttons) =>
buttons.map((e) => {
return { id: e.id, disabled: false }
})
)
} catch (err) {
console.log(err)
}
}
fetchQueries()
}, 500)
return () => clearTimeout(delayDebounceFn)
}, [])
useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('diskSpaceMonit', (data) => {
if (data.action === 'update') {
setDiskSpaceInfo(data.diskSpace)
}
})
socket.on('settings', (data) => {
if (data.action === 'update') {
setSettings((prevState) => {
const aux = [...prevState]
const settingIndex = aux.findIndex((s) => s.key === data.setting.key)
aux[settingIndex].value = data.setting.value
return aux
})
}
})
return () => {
socket.disconnect()
}
}, [])
return (
<Can
role={user.profile}
perform="connections-view:show"
yes={() => (
<MainContainer>
<ConfirmationModal
title={confirmModalInfo.title}
open={confirmModalOpen}
onClose={setConfirmModalOpen}
onConfirm={handleSubmitConfirmationModal}
>
{confirmModalInfo.message}
</ConfirmationModal>
<QrcodeModal
open={qrModalOpen}
onClose={handleCloseQrModal}
campaignId={!campaignModalOpen && selectedCampaign?.id}
/>
<CampaignModal
open={campaignModalOpen}
onClose={handleCloseCampaignModal}
campaignId={selectedCampaign?.id}
dispatch={dispatch}
/>
<MainHeader>
<Title>Campanhas</Title>
<MainHeaderButtonsWrapper>
<Can
role={user.profile}
perform="btn-add-whatsapp"
updatedAt
yes={() => (
<Button
variant="contained"
color="primary"
onClick={handleOpenCampaignModal}
>
criar campanha
</Button>
)}
/>
</MainHeaderButtonsWrapper>
</MainHeader>
<Paper className={classes.mainPaper} variant="outlined">
<>
<Table size="small">
<TableHead>
<TableRow>
<TableCell align="center">
Nome
</TableCell>
<TableCell align="center">
Status
</TableCell>
<Can
role={user.profile}
perform="connection-button:show"
yes={() => (
<TableCell align="center">
{i18n.t('connections.table.session')}
</TableCell>
)}
/>
<TableCell align="center">
Whatsapp sender
</TableCell>
<TableCell align="center">
Mensagem
</TableCell>
<TableCell align="center">
{i18n.t('connections.table.actions')}
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{loading ? (
<TableRowSkeleton />
) : (
<>
{campaigns?.length > 0 &&
campaigns.map((campaign) => (
<TableRow key={campaign.id}>
<TableCell align="center">
{campaign.name}
</TableCell>
<TableCell align="center">
Status
</TableCell>
<Can
role={user.profile}
perform="connection-button:show"
yes={() => (
<TableCell align="center">
{renderActionButtons(campaign)}
</TableCell>
)}
/>
<TableCell align="center">
{campaign.whatsapp_sender}
</TableCell>
<TableCell align="center">
{campaign.message}
</TableCell>
<TableCell align="center">
<Can
role={user.profile}
perform="show-icon-edit-whatsapp"
yes={() => (
<div
style={{
margin: 0,
padding: 0,
border: 'none',
display: 'inline',
}}
>
{(settings &&
settings.length > 0 &&
getSettingValue('editURA') &&
getSettingValue('editURA') ===
'enabled') |
(user.profile === 'master') ? (
<IconButton
size="small"
onClick={() =>
handleEditCampaign(campaign)
}
>
<Edit />
</IconButton>
) : (
<div></div>
)}
</div>
)}
/>
<Can
role={user.profile}
perform="btn-remove-whatsapp"
yes={() => (
<IconButton
size="small"
onClick={(e) => {
handleOpenConfirmationModal(
'delete',
campaign.id
)
}}
>
<DeleteOutline />
</IconButton>
)}
/>
</TableCell>
</TableRow>
))}
</>
)}
</TableBody>
</Table>
</>
</Paper>
</MainContainer>
)}
/>
)
}
export default Campaign

View File

@ -1,43 +1,43 @@
import React, { useState, useEffect, useReducer, useContext } from "react"; import React, { useState, useEffect, useReducer, useContext } from "react"
import openSocket from "socket.io-client"; import openSocket from "socket.io-client"
import { toast } from "react-toastify"; import { toast } from "react-toastify"
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom"
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles"
import Table from "@material-ui/core/Table"; import Table from "@material-ui/core/Table"
import TableBody from "@material-ui/core/TableBody"; import TableBody from "@material-ui/core/TableBody"
import TableCell from "@material-ui/core/TableCell"; import TableCell from "@material-ui/core/TableCell"
import TableHead from "@material-ui/core/TableHead"; import TableHead from "@material-ui/core/TableHead"
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow"
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper"
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button"
import Avatar from "@material-ui/core/Avatar"; import Avatar from "@material-ui/core/Avatar"
import WhatsAppIcon from "@material-ui/icons/WhatsApp"; import WhatsAppIcon from "@material-ui/icons/WhatsApp"
import SearchIcon from "@material-ui/icons/Search"; import SearchIcon from "@material-ui/icons/Search"
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField"
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment"
import IconButton from "@material-ui/core/IconButton"; import IconButton from "@material-ui/core/IconButton"
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline"; import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline"
import EditIcon from "@material-ui/icons/Edit"; import EditIcon from "@material-ui/icons/Edit"
import api from "../../services/api"; import api from "../../services/api"
import TableRowSkeleton from "../../components/TableRowSkeleton"; import TableRowSkeleton from "../../components/TableRowSkeleton"
import ContactModal from "../../components/ContactModal"; import ContactModal from "../../components/ContactModal"
import ConfirmationModal from "../../components/ConfirmationModal/"; import ConfirmationModal from "../../components/ConfirmationModal/"
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n"
import MainHeader from "../../components/MainHeader"; import MainHeader from "../../components/MainHeader"
import Title from "../../components/Title"; import Title from "../../components/Title"
import MainHeaderButtonsWrapper from "../../components/MainHeaderButtonsWrapper"; import MainHeaderButtonsWrapper from "../../components/MainHeaderButtonsWrapper"
import MainContainer from "../../components/MainContainer"; import MainContainer from "../../components/MainContainer"
import toastError from "../../errors/toastError"; import toastError from "../../errors/toastError"
import { AuthContext } from "../../context/Auth/AuthContext"; import { AuthContext } from "../../context/Auth/AuthContext"
import { Can } from "../../components/Can"; import { Can } from "../../components/Can"
import apiBroker from "../../services/apiBroker"; import apiBroker from "../../services/apiBroker"
import fileDownload from 'js-file-download' import fileDownload from 'js-file-download'
import ContactCreateTicketModal from "../../components/ContactCreateTicketModal"; import ContactCreateTicketModal from "../../components/ContactCreateTicketModal"
@ -46,50 +46,50 @@ const reducer = (state, action) => {
if (action.type === "LOAD_CONTACTS") { if (action.type === "LOAD_CONTACTS") {
const contacts = action.payload; const contacts = action.payload
const newContacts = []; const newContacts = []
contacts.forEach((contact) => { contacts.forEach((contact) => {
const contactIndex = state.findIndex((c) => +c.id === +contact.id); const contactIndex = state.findIndex((c) => +c.id === +contact.id)
if (contactIndex !== -1) { if (contactIndex !== -1) {
state[contactIndex] = contact; state[contactIndex] = contact
} else { } else {
newContacts.push(contact); newContacts.push(contact)
} }
}); })
return [...state, ...newContacts]; return [...state, ...newContacts]
} }
if (action.type === "UPDATE_CONTACTS") { if (action.type === "UPDATE_CONTACTS") {
const contact = action.payload; const contact = action.payload
const contactIndex = state.findIndex((c) => +c.id === +contact.id); const contactIndex = state.findIndex((c) => +c.id === +contact.id)
if (contactIndex !== -1) { if (contactIndex !== -1) {
state[contactIndex] = contact; state[contactIndex] = contact
return [...state]; return [...state]
} else { } else {
return [contact, ...state]; return [contact, ...state]
} }
} }
if (action.type === "DELETE_CONTACT") { if (action.type === "DELETE_CONTACT") {
const contactId = action.payload; const contactId = action.payload
const contactIndex = state.findIndex((c) => +c.id === +contactId); const contactIndex = state.findIndex((c) => +c.id === +contactId)
if (contactIndex !== -1) { if (contactIndex !== -1) {
state.splice(contactIndex, 1); state.splice(contactIndex, 1)
} }
return [...state]; return [...state]
} }
if (action.type === "RESET") { if (action.type === "RESET") {
return []; return []
} }
}; }
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
mainPaper: { mainPaper: {
@ -98,24 +98,24 @@ const useStyles = makeStyles((theme) => ({
overflowY: "scroll", overflowY: "scroll",
...theme.scrollbarStyles, ...theme.scrollbarStyles,
}, },
})); }))
const Contacts = () => { const Contacts = () => {
const classes = useStyles(); const classes = useStyles()
const history = useHistory(); const history = useHistory()
const { user } = useContext(AuthContext); const { user } = useContext(AuthContext)
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false)
const [pageNumber, setPageNumber] = useState(1); const [pageNumber, setPageNumber] = useState(1)
const [searchParam, setSearchParam] = useState(""); const [searchParam, setSearchParam] = useState("")
const [contacts, dispatch] = useReducer(reducer, []); const [contacts, dispatch] = useReducer(reducer, [])
const [selectedContactId, setSelectedContactId] = useState(null); const [selectedContactId, setSelectedContactId] = useState(null)
const [contactModalOpen, setContactModalOpen] = useState(false); const [contactModalOpen, setContactModalOpen] = useState(false)
const [isCreateTicketModalOpen, setIsCreateTicketModalOpen] = useState(false) const [isCreateTicketModalOpen, setIsCreateTicketModalOpen] = useState(false)
const [deletingContact, setDeletingContact] = useState(null); const [deletingContact, setDeletingContact] = useState(null)
const [confirmOpen, setConfirmOpen] = useState(false); const [confirmOpen, setConfirmOpen] = useState(false)
const [hasMore, setHasMore] = useState(false); const [hasMore, setHasMore] = useState(false)
const [onQueueStatus, setOnQueueProcessStatus] = useState(undefined) const [onQueueStatus, setOnQueueProcessStatus] = useState(undefined)
@ -132,20 +132,20 @@ const Contacts = () => {
return return
} }
const formData = new FormData(); const formData = new FormData()
formData.append("adminId", user.id); formData.append("adminId", user.id)
formData.append("baseURL", process.env.REACT_APP_BACKEND_URL_PRIVATE,); formData.append("baseURL", process.env.REACT_APP_BACKEND_URL_PRIVATE,)
formData.append("frontURL", process.env.REACT_APP_FRONTEND_URL); formData.append("frontURL", process.env.REACT_APP_FRONTEND_URL)
formData.append("identifier", 'contacts_insert_csv'); formData.append("identifier", 'contacts_insert_csv')
formData.append("file", event.target.files[0]); formData.append("file", event.target.files[0])
const config = { const config = {
headers: { headers: {
'content-type': 'multipart/form-data', 'content-type': 'multipart/form-data',
}, },
}; }
const bulk_contact_insert = await apiBroker.post("/contacts/bulk/insert", formData, config); const bulk_contact_insert = await apiBroker.post("/contacts/bulk/insert", formData, config)
console.log(bulk_contact_insert.data) console.log(bulk_contact_insert.data)
@ -156,7 +156,7 @@ const Contacts = () => {
// history.go(0); // history.go(0);
} catch (err) { } catch (err) {
toastError(err); toastError(err)
} }
} }
@ -177,7 +177,7 @@ const Contacts = () => {
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE, baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
identifier: 'contacts_insert_csv' identifier: 'contacts_insert_csv'
} }
}); })
if (insertOnQueue && insertOnQueue.data) { if (insertOnQueue && insertOnQueue.data) {
@ -193,23 +193,23 @@ const Contacts = () => {
} catch (err) { } catch (err) {
console.log(err); console.log(err)
} }
}; }
fetchReportOnQueue(); fetchReportOnQueue()
}, 500); }, 500)
return () => clearTimeout(delayDebounceFn); return () => clearTimeout(delayDebounceFn)
}, [user]) }, [user])
useEffect(() => { useEffect(() => {
dispatch({ type: "RESET" }); dispatch({ type: "RESET" })
setPageNumber(1); setPageNumber(1)
}, [searchParam]); }, [searchParam])
useEffect(() => { useEffect(() => {
@ -220,7 +220,7 @@ const Contacts = () => {
setLoading(true); setLoading(true)
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
const fetchContacts = async () => { const fetchContacts = async () => {
@ -230,25 +230,25 @@ const Contacts = () => {
return return
} }
const { data } = await api.get("/contacts/", { params: { searchParam, pageNumber }, }); const { data } = await api.get("/contacts/", { params: { searchParam, pageNumber }, })
dispatch({ type: "LOAD_CONTACTS", payload: data.contacts }); dispatch({ type: "LOAD_CONTACTS", payload: data.contacts })
setHasMore(data.hasMore); setHasMore(data.hasMore)
setLoading(false); setLoading(false)
} catch (err) { } catch (err) {
toastError(err); toastError(err)
} }
}; }
fetchContacts(); fetchContacts()
}, 500); }, 500)
return () => clearTimeout(delayDebounceFn); return () => clearTimeout(delayDebounceFn)
}, [searchParam, pageNumber]); }, [searchParam, pageNumber])
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on("contactsBulkInsertOnQueueStatus", (data) => { socket.on("contactsBulkInsertOnQueueStatus", (data) => {
if (data.action === 'update') { if (data.action === 'update') {
@ -261,7 +261,7 @@ const Contacts = () => {
setOnQueueProcessStatus(data.insertOnQueue.queueStatus) setOnQueueProcessStatus(data.insertOnQueue.queueStatus)
if (data.insertOnQueue.queueStatus === "success") { if (data.insertOnQueue.queueStatus === "success") {
history.go(0); history.go(0)
} }
} }
@ -270,18 +270,18 @@ const Contacts = () => {
socket.on("contact", (data) => { socket.on("contact", (data) => {
if (data.action === "update" || data.action === "create") { if (data.action === "update" || data.action === "create") {
dispatch({ type: "UPDATE_CONTACTS", payload: data.contact }); dispatch({ type: "UPDATE_CONTACTS", payload: data.contact })
} }
if (data.action === "delete") { if (data.action === "delete") {
dispatch({ type: "DELETE_CONTACT", payload: +data.contactId }); dispatch({ type: "DELETE_CONTACT", payload: +data.contactId })
} }
}); })
return () => { return () => {
socket.disconnect(); socket.disconnect()
}; }
}, [user, history]); }, [user, history])
const removeExtraSpace = (str) => { const removeExtraSpace = (str) => {
@ -291,18 +291,18 @@ const Contacts = () => {
} }
const handleSearch = (event) => { const handleSearch = (event) => {
setSearchParam(removeExtraSpace(event.target.value.toLowerCase())); setSearchParam(removeExtraSpace(event.target.value.toLowerCase()))
}; }
const handleOpenContactModal = () => { const handleOpenContactModal = () => {
setSelectedContactId(null); setSelectedContactId(null)
setContactModalOpen(true); setContactModalOpen(true)
}; }
const handleCloseContactModal = () => { const handleCloseContactModal = () => {
setSelectedContactId(null); setSelectedContactId(null)
setContactModalOpen(false); setContactModalOpen(false)
}; }
const handleOpenCreateTicketModal = (contactId) => { const handleOpenCreateTicketModal = (contactId) => {
setSelectedContactId(contactId) setSelectedContactId(contactId)
@ -330,46 +330,46 @@ const Contacts = () => {
// }; // };
const hadleEditContact = (contactId) => { const hadleEditContact = (contactId) => {
setSelectedContactId(contactId); setSelectedContactId(contactId)
setContactModalOpen(true); setContactModalOpen(true)
}; }
const handleDeleteContact = async (contactId) => { const handleDeleteContact = async (contactId) => {
try { try {
await api.delete(`/contacts/${contactId}`); await api.delete(`/contacts/${contactId}`)
toast.success(i18n.t("contacts.toasts.deleted")); toast.success(i18n.t("contacts.toasts.deleted"))
} catch (err) { } catch (err) {
toastError(err); toastError(err)
} }
setDeletingContact(null); setDeletingContact(null)
setSearchParam(""); setSearchParam("")
setPageNumber(1); setPageNumber(1)
}; }
const handleimportContact = async () => { const handleimportContact = async () => {
try { try {
await api.post("/contacts/import"); await api.post("/contacts/import")
history.go(0); history.go(0)
} catch (err) { } catch (err) {
toastError(err); toastError(err)
} }
}; }
const loadMore = () => { const loadMore = () => {
setPageNumber((prevState) => prevState + 1); setPageNumber((prevState) => prevState + 1)
}; }
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()
} }
}; }
const handleDownload = async () => { const handleDownload = async () => {
@ -378,16 +378,16 @@ const Contacts = () => {
try { try {
let res = await apiBroker.get(`/contacts/download/${zipfile}`, { responseType: 'blob' }); let res = await apiBroker.get(`/contacts/download/${zipfile}`, { responseType: 'blob' })
if (res) { if (res) {
fileDownload(res.data, `${zipfile}`); fileDownload(res.data, `${zipfile}`)
setOnQueueProcessStatus('empty') setOnQueueProcessStatus('empty')
} }
} catch (err) { } catch (err) {
console.log(err); console.log(err)
} }
@ -406,7 +406,7 @@ const Contacts = () => {
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE, baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
identifier: 'contacts_insert_csv' identifier: 'contacts_insert_csv'
} }
}); })
if (res) { if (res) {
setOnQueueProcessStatus('empty') setOnQueueProcessStatus('empty')
@ -414,7 +414,7 @@ const Contacts = () => {
} catch (err) { } catch (err) {
console.log(err); console.log(err)
} }
@ -452,13 +452,13 @@ const Contacts = () => {
> >
{"CSV ALL"} {"CSV ALL"}
</Button> */} </Button> */}
</>); </>)
case 'pending' || 'processing': case 'pending' || 'processing':
return ( return (
<> <>
<span>PROCESSING...</span> <span>PROCESSING...</span>
</>); </>)
case 'success': case 'success':
return ( return (
@ -472,7 +472,7 @@ const Contacts = () => {
> >
{'DOWNLOAD'} {'DOWNLOAD'}
</Button> </Button>
</>); </>)
case 'error': case 'error':
return ( return (
<> <>
@ -485,16 +485,16 @@ const Contacts = () => {
> >
{'ERROR'} {'ERROR'}
</Button> </Button>
</>); </>)
case 'downloading': case 'downloading':
return ( return (
<> <>
<span>DOWNLOADING...</span> <span>DOWNLOADING...</span>
</>); </>)
default: default:
return (<><span>WAITING...</span></>); return (<><span>WAITING...</span></>)
} }
} }
@ -642,8 +642,8 @@ const Contacts = () => {
<IconButton <IconButton
size="small" size="small"
onClick={(e) => { onClick={(e) => {
setConfirmOpen(true); setConfirmOpen(true)
setDeletingContact(contact); setDeletingContact(contact)
}} }}
> >
<DeleteOutlineIcon /> <DeleteOutlineIcon />
@ -659,7 +659,7 @@ const Contacts = () => {
</Table> </Table>
</Paper> </Paper>
</MainContainer> </MainContainer>
); )
}; }
export default Contacts; export default Contacts

View File

@ -1,27 +1,26 @@
import React from "react"; 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 LoggedInLayout from "../layout"; import LoggedInLayout from '../layout'
import Dashboard from "../pages/Dashboard/"; import Dashboard from '../pages/Dashboard/'
import Report from "../pages/Report/";
import SchedulesReminder from "../pages/SchedulesReminder/";
import Tickets from "../pages/Tickets/";
import Signup from "../pages/Signup/";
import Login from "../pages/Login/";
import Connections from "../pages/Connections/";
import Settings from "../pages/Settings/";
import Users from "../pages/Users";
import Contacts from "../pages/Contacts/";
import QuickAnswers from "../pages/QuickAnswers/";
import Queues from "../pages/Queues/";
import { AuthProvider } from "../context/Auth/AuthContext";
import { WhatsAppsProvider } from "../context/WhatsApp/WhatsAppsContext";
import Route from "./Route";
import Report from '../pages/Report/'
import SchedulesReminder from '../pages/SchedulesReminder/'
import Tickets from '../pages/Tickets/'
import Signup from '../pages/Signup/'
import Login from '../pages/Login/'
import Connections from '../pages/Connections/'
import Campaign from '../pages/Campaign'
import Settings from '../pages/Settings/'
import Users from '../pages/Users'
import Contacts from '../pages/Contacts/'
import QuickAnswers from '../pages/QuickAnswers/'
import Queues from '../pages/Queues/'
import { AuthProvider } from '../context/Auth/AuthContext'
import { WhatsAppsProvider } from '../context/WhatsApp/WhatsAppsContext'
import Route from './Route'
const Routes = () => { const Routes = () => {
return ( return (
@ -33,27 +32,48 @@ const Routes = () => {
<WhatsAppsProvider> <WhatsAppsProvider>
<LoggedInLayout> <LoggedInLayout>
<Route exact path="/" component={Dashboard} isPrivate /> <Route exact path="/" component={Dashboard} isPrivate />
<Route exact path="/tickets/:ticketId?" component={Tickets} isPrivate /> <Route
exact
path="/tickets/:ticketId?"
component={Tickets}
isPrivate
/>
<Route exact path="/connections" component={Connections} isPrivate /> <Route
exact
path="/connections"
component={Connections}
isPrivate
/>
<Route exact path="/report" component={Report} isPrivate /> <Route exact path="/report" component={Report} isPrivate />
<Route exact path="/contacts" component={Contacts} isPrivate /> <Route exact path="/contacts" component={Contacts} isPrivate />
<Route exact path="/schedulesReminder" component={SchedulesReminder} isPrivate /> <Route
exact
path="/schedulesReminder"
component={SchedulesReminder}
isPrivate
/>
<Route exact path="/users" component={Users} isPrivate /> <Route exact path="/users" component={Users} isPrivate />
<Route exact path="/quickAnswers" component={QuickAnswers} isPrivate /> <Route
exact
path="/quickAnswers"
component={QuickAnswers}
isPrivate
/>
<Route exact path="/Settings" component={Settings} isPrivate /> <Route exact path="/Settings" component={Settings} isPrivate />
<Route exact path="/Queues" component={Queues} isPrivate /> <Route exact path="/Queues" component={Queues} isPrivate />
<Route exact path="/campaign" component={Campaign} isPrivate />
</LoggedInLayout> </LoggedInLayout>
</WhatsAppsProvider> </WhatsAppsProvider>
</Switch> </Switch>
<ToastContainer autoClose={3000} /> <ToastContainer autoClose={3000} />
</AuthProvider> </AuthProvider>
</BrowserRouter> </BrowserRouter>
); )
}; }
export default Routes; export default Routes