Finalização do modulo campanha

pull/21/head
adriano 2023-08-14 15:39:05 -03:00
parent 75eb24fe62
commit 5907fc6cb2
4 changed files with 149 additions and 299 deletions

View File

@ -75,7 +75,7 @@ export const initIO = (httpServer: Server): SocketIO => {
}); });
socket.on("campaign_message_sent", async (data: any) => { socket.on("campaign_message_sent", async (data: any) => {
console.log("campaign_message_sent: ", data); // console.log("campaign_message_sent: ", data);
const io = getIO(); const io = getIO();

View File

@ -1,21 +1,19 @@
import React, { useState, useEffect, useContext, createContext } from 'react' import React, { useState, useEffect, useContext, } from 'react'
import * as Yup from 'yup' import * as Yup from 'yup'
import { Formik, Form, Field } from 'formik' import { Formik, Form, Field, } from 'formik'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import { green } from '@material-ui/core/colors' import { green } from '@material-ui/core/colors'
import { AuthContext } from '../../context/Auth/AuthContext' import { AuthContext } from '../../context/Auth/AuthContext'
import { Can } from '../../components/Can'
import apiBroker from '../../services/apiBroker' import apiBroker from '../../services/apiBroker'
import Select from "@material-ui/core/Select" import Select from "@material-ui/core/Select"
import MenuItem from "@material-ui/core/MenuItem" import MenuItem from "@material-ui/core/MenuItem"
import SelectField from "../../components/Report/SelectField"
import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext" import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext"
import { import {
@ -25,15 +23,11 @@ import {
Button, Button,
DialogActions, DialogActions,
CircularProgress, CircularProgress,
TextField, TextField,
Switch,
FormControlLabel,
} from '@material-ui/core' } from '@material-ui/core'
import api from '../../services/api'
import { i18n } from '../../translate/i18n' import { i18n } from '../../translate/i18n'
import toastError from '../../errors/toastError' import toastError from '../../errors/toastError'
import QueueSelect from '../QueueSelect'
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
root: { root: {
@ -67,6 +61,23 @@ const SessionSchema = Yup.object().shape({
.min(2, 'Too Short!') .min(2, 'Too Short!')
.max(100, 'Too Long!') .max(100, 'Too Long!')
.required('Required'), .required('Required'),
secondStart: Yup.number()
.required('Min time is required')
.min(3, 'Min time must be 3')
.max(3600, 'Min must be less than 3600'),
secondEnd: Yup.number()
.required('Max time is required')
.min(3, 'Min time must be 3')
.max(3600, 'Max must be less than 3600')
.test('higher-than-lower', 'Tempo final deve ser maior que tempo inicio!', function (
secondEnd
) {
const secondStart = this.resolve(Yup.ref('secondStart'))
return secondStart === undefined || secondEnd === undefined || secondEnd > secondStart
}),
}) })
const CampaignModal = ({ open, onClose, campaignId, dispatch }) => { const CampaignModal = ({ open, onClose, campaignId, dispatch }) => {
@ -77,27 +88,21 @@ const CampaignModal = ({ open, onClose, campaignId, dispatch }) => {
status: 'pending', status: 'pending',
whatsapp_sender: '', whatsapp_sender: '',
csv_original_file_name: '', csv_original_file_name: '',
secondStart: '3',
secondEnd: '15',
textToSeconds: true,
} }
const { user } = useContext(AuthContext) const { user } = useContext(AuthContext)
const { whatsApps } = useContext(WhatsAppsContext) const { whatsApps } = useContext(WhatsAppsContext)
console.log('------------> whatsApps: ', whatsApps)
const [campaign, setCampaign] = useState(initialState) const [campaign, setCampaign] = useState(initialState)
// const [selectedQueueIds, setSelectedQueueIds] = useState([]) // const [selectedQueueIds, setSelectedQueueIds] = useState([])
const [file, setFile] = useState() const [file, setFile] = useState()
const [selectedNumber, setSelectedNumber] = useState('') const [selectedNumber, setSelectedNumber] = useState('')
const [itemHover, setItemHover] = useState(-1)
useEffect(() => {
console.log('selectedNumber: ', selectedNumber)
}, [selectedNumber])
useEffect(() => { useEffect(() => {
const fetchSession = async () => { const fetchSession = async () => {
@ -122,7 +127,7 @@ const CampaignModal = ({ open, onClose, campaignId, dispatch }) => {
} }
} }
fetchSession() fetchSession()
}, [campaignId]) }, [campaignId, user.id])
const handleSaveCampaign = async (values) => { const handleSaveCampaign = async (values) => {
let response = null let response = null
@ -135,6 +140,9 @@ const CampaignModal = ({ open, onClose, campaignId, dispatch }) => {
formData.append('file', file) formData.append('file', file)
formData.append('name', values.name) formData.append('name', values.name)
formData.append('whatsapp_sender', selectedNumber) formData.append('whatsapp_sender', selectedNumber)
formData.append('secondStart', values.secondStart)
formData.append('secondEnd', values.secondEnd)
formData.append('textToSeconds', values.textToSeconds)
formData.append('message', values.message) formData.append('message', values.message)
formData.append('csv_original_file_name', file?.name) formData.append('csv_original_file_name', file?.name)
@ -185,15 +193,15 @@ const CampaignModal = ({ open, onClose, campaignId, dispatch }) => {
alert('Arquivo não pode ser maior que 4 MB!') alert('Arquivo não pode ser maior que 4 MB!')
return return
} }
console.log('event.target.files[0]: ', event.target.files[0])
setFile(event.target.files[0]) setFile(event.target.files[0])
} catch (err) { } catch (err) {
toastError(err) toastError(err)
} }
} }
return ( return (
<div className={classes.root}> <div className={classes.root}>
<Dialog <Dialog
@ -221,51 +229,41 @@ const CampaignModal = ({ open, onClose, campaignId, dispatch }) => {
<Form> <Form>
<DialogContent dividers> <DialogContent dividers>
<Can <div>
role={user.profile} <div className={classes.multFieldLine}>
perform="url-remote-session:show" <Field
yes={() => ( as={TextField}
<> label={i18n.t('whatsappModal.form.name')}
<div> autoFocus
<div className={classes.multFieldLine}> name="name"
<Field error={touched.name && Boolean(errors.name)}
as={TextField} helperText={touched.name && errors.name}
label={i18n.t('whatsappModal.form.name')} variant="outlined"
autoFocus margin="dense"
name="name" className={classes.textField}
error={touched.name && Boolean(errors.name)} />
helperText={touched.name && errors.name}
variant="outlined"
margin="dense"
className={classes.textField}
/>
<Select <Select
value={selectedNumber} value={selectedNumber}
onChange={(e) => setSelectedNumber(e.target.value)} onChange={(e) => setSelectedNumber(e.target.value)}
label={i18n.t("transferTicketModal.fieldQueuePlaceholder")} label={i18n.t("transferTicketModal.fieldQueuePlaceholder")}
required required
> >
<MenuItem style={{ background: "white", }} value={''}>&nbsp;</MenuItem> <MenuItem style={{ background: "white", }} value={''}>&nbsp;</MenuItem>
{whatsApps.map((whatsapp) => ( {whatsApps.map((whatsapp) => (
<MenuItem <MenuItem
key={whatsapp.id} key={whatsapp.id}
value={whatsapp.number} value={whatsapp.number}
onMouseEnter={() => setItemHover(whatsapp.id)} >{whatsapp.number}
onMouseLeave={() => setItemHover(-1)} </MenuItem>
>{whatsapp.number} ))}
</MenuItem> </Select>
))} </div>
</Select>
</div>
<div className={classes.multFieldLine}> <div className={classes.multFieldLine}>
</div> </div>
</div> </div>
</>
)}
/>
<div> <div>
<Field <Field
@ -309,6 +307,39 @@ const CampaignModal = ({ open, onClose, campaignId, dispatch }) => {
<h3>{file?.name || campaign?.csv_original_file_name}</h3> <h3>{file?.name || campaign?.csv_original_file_name}</h3>
</div> </div>
<div className={classes.multFieldLine} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Field
as={TextField}
label='Inicio em segundos'
autoFocus
name="secondStart"
error={touched.secondStart && Boolean(errors.secondStart)}
helperText={touched.secondStart && errors.secondStart}
variant="outlined"
margin="dense"
className={classes.textField}
/>
<Field
as={TextField}
label='Fim em segundos'
autoFocus
name="secondEnd"
error={touched.secondEnd && Boolean(errors.secondEnd)}
helperText={touched.secondEnd && errors.secondEnd}
variant="outlined"
margin="dense"
className={classes.textField}
/>
<label>
<Field type="checkbox" name="textToSeconds"/>
Tamanho do texto para segundos
</label>
</div>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button

View File

@ -9,7 +9,10 @@ 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 CampaignIcon from '@material-ui/icons/Send';
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";
@ -145,7 +148,7 @@ const MainListItems = (props) => {
<ListItemLink <ListItemLink
to="/campaign" to="/campaign"
primary="Campanha" primary="Campanha"
icon={<ReportOutlinedIcon />} icon={<CampaignIcon />}
/> />
<Can <Can

View File

@ -1,6 +1,5 @@
import React, { useState, useCallback, useEffect, useReducer, useContext } from 'react' import React, { useState, useCallback, useEffect, useReducer, useContext } from 'react'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { format, parseISO } from 'date-fns'
import openSocket from 'socket.io-client' import openSocket from 'socket.io-client'
@ -14,19 +13,11 @@ import {
IconButton, IconButton,
Table, Table,
TableHead, TableHead,
Paper, Paper,
Tooltip,
Typography,
CircularProgress,
} from '@material-ui/core' } from '@material-ui/core'
import { import {
Edit, Edit,
CheckCircle, DeleteOutline,
SignalCellularConnectedNoInternet2Bar,
SignalCellularConnectedNoInternet0Bar,
SignalCellular4Bar,
CropFree,
DeleteOutline,
// Restore // Restore
} from '@material-ui/icons' } from '@material-ui/icons'
@ -61,9 +52,7 @@ const reducer = (state, action) => {
const campaigns = action.payload const campaigns = action.payload
return [...state, ...campaigns] return [...state, ...campaigns]
} }
if (action.type === "UPDATE_CAMPAIGNS") { if (action.type === "UPDATE_CAMPAIGNS") {
console.log('STATE: ', state)
const campaign = action.payload const campaign = action.payload
const campaignIndex = state.findIndex((c) => c.id === campaign.id) const campaignIndex = state.findIndex((c) => c.id === campaign.id)
@ -121,31 +110,7 @@ const useStyles = makeStyles((theme) => ({
buttonProgress: { buttonProgress: {
color: green[500], 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 Campaign = () => {
//-------- //--------
@ -153,24 +118,14 @@ const Campaign = () => {
const classes = useStyles() const classes = useStyles()
// const { whatsApps } = useContext(WhatsAppsContext) // const { whatsApps } = useContext(WhatsAppsContext)
// console.log('------------> whatsApps: ', whatsApps)
// const { campaigns, loading } = useContext(WhatsAppsContext) // const { campaigns, loading } = useContext(WhatsAppsContext)
const [campaignModalOpen, setCampaignModalOpen] = useState(false) const [campaignModalOpen, setCampaignModalOpen] = useState(false)
const [qrModalOpen, setQrModalOpen] = useState(false) const [qrModalOpen, setQrModalOpen] = useState(false)
const [selectedCampaign, setSelectedCampaign] = useState(null) const [selectedCampaign, setSelectedCampaign] = useState(null)
const [confirmModalOpen, setConfirmModalOpen] = useState(false) 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 [campaigns, dispatch] = useReducer(reducer, [])
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
@ -198,8 +153,7 @@ const Campaign = () => {
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
const fetchContacts = async () => { const fetchContacts = async () => {
try { try {
console.log('process.env.REACT_APP_BACKEND_URL_PRIVATE: ', process.env.REACT_APP_BACKEND_URL_PRIVATE)
const { data } = await apiBroker.get('/campaign', { const { data } = await apiBroker.get('/campaign', {
params: { params: {
@ -207,9 +161,7 @@ const Campaign = () => {
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE, baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
identifier: 'campaign' identifier: 'campaign'
} }
}) })
console.log('data.campaign : ', data.campaign)
dispatch({ type: "LOAD_CAMPAIGNS", payload: data.campaign }) dispatch({ type: "LOAD_CAMPAIGNS", payload: data.campaign })
setLoading(false) setLoading(false)
@ -222,7 +174,7 @@ const Campaign = () => {
fetchContacts() fetchContacts()
}, 500) }, 500)
return () => clearTimeout(delayDebounceFn) return () => clearTimeout(delayDebounceFn)
}, []) }, [user.id])
useEffect(() => { useEffect(() => {
const fetchSession = async () => { const fetchSession = async () => {
@ -245,37 +197,8 @@ const Campaign = () => {
} }
} }
fetchSession() 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 = () => { const handleOpenCampaignModal = () => {
setSelectedCampaign(null) setSelectedCampaign(null)
@ -285,20 +208,12 @@ const Campaign = () => {
const handleCloseCampaignModal = useCallback(() => { const handleCloseCampaignModal = useCallback(() => {
setCampaignModalOpen(false) setCampaignModalOpen(false)
setSelectedCampaign(null) setSelectedCampaign(null)
}, [setSelectedCampaign, setCampaignModalOpen]) }, [setSelectedCampaign, setCampaignModalOpen])
const handleOpenQrModal = (campaign) => { const handleStart = async (campaign) => {
setSelectedCampaign(campaign)
setQrModalOpen(true)
}
const handleStart = async (campaign) => {
console.log('start')
try { try {
const { data } = await apiBroker.post(`/campaign/start/${campaign.id}`) const { data } = await apiBroker.post(`/campaign/start/${campaign.id}`)
console.log('==============> data.campaign: ', data.campaign)
dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign }) dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign })
@ -309,13 +224,10 @@ const Campaign = () => {
} }
} }
const handleStop = async (campaign) => { const handleStop = async (campaign) => {
console.log('stop')
try { try {
const { data } = await apiBroker.post(`/campaign/stop/${campaign.id}`) const { data } = await apiBroker.post(`/campaign/stop/${campaign.id}`)
console.log('==============> data.campaign: ', data.campaign)
dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign }) dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign })
@ -412,80 +324,7 @@ const Campaign = () => {
)} )}
</> </>
) )
} }
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(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
@ -506,36 +345,19 @@ const Campaign = () => {
} }
}) })
socket.on('campaign', (data) => { socket.on('campaign', (data) => {
console.log('------------> CAMPAIGN: ', data)
if (data.action === 'update') { if (data.action === 'update') {
dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign }) dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign })
} }
}) })
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 () => { return () => {
socket.disconnect() socket.disconnect()
} }
}, []) }, [user.id])
return ( return (
<Can <Can
@ -569,20 +391,13 @@ const Campaign = () => {
<Title>Campanhas</Title> <Title>Campanhas</Title>
<MainHeaderButtonsWrapper> <MainHeaderButtonsWrapper>
<Can <Button
role={user.profile} variant="contained"
perform="btn-add-whatsapp" color="primary"
updatedAt onClick={handleOpenCampaignModal}
yes={() => ( >
<Button criar campanha
variant="contained" </Button>
color="primary"
onClick={handleOpenCampaignModal}
>
criar campanha
</Button>
)}
/>
</MainHeaderButtonsWrapper> </MainHeaderButtonsWrapper>
</MainHeader> </MainHeader>
@ -661,17 +476,17 @@ const Campaign = () => {
{campaign.message} {campaign.message}
</TableCell> </TableCell>
<TableCell align="center"> {campaign.status !== 'running' ? <TableCell align="center">
{campaign.status != 'success' && ( {campaign.status !== 'success' && (
<IconButton <IconButton
size="small" size="small"
onClick={() => onClick={() =>
handleEditCampaign(campaign) handleEditCampaign(campaign)
} }
> >
<Edit /> <Edit />
</IconButton>)} </IconButton>)}
<IconButton <IconButton
@ -685,7 +500,8 @@ const Campaign = () => {
> >
<DeleteOutline /> <DeleteOutline />
</IconButton> </IconButton>
</TableCell> </TableCell> : <TableCell align="center"></TableCell>}
</TableRow> </TableRow>