From e8eb00e96c6febb05238887513ed8102c91d77fa Mon Sep 17 00:00:00 2001 From: adriano Date: Sat, 12 Mar 2022 02:13:15 -0300 Subject: [PATCH] =?UTF-8?q?Cria=C3=A7=C3=A3o=20do=20agendamento=20com=20op?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20excluira=20agendametnos=20anteriores?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/SchedulingNotifyController.ts | 15 + backend/src/controllers/TicketController.ts | 1 + ...20220224003939-create-scheduling-notify.ts | 4 + backend/src/models/SchedulingNotify.ts | 3 + backend/src/routes/SchedulingNotifyRoutes.ts | 10 + backend/src/routes/index.ts | 2 + .../CreateSchedulingNotifyService.ts | 3 + .../ListSchedulingNotifyContactService.ts | 3 +- .../components/ChatEnd/ModalChatEnd/index.js | 428 ++++++++++-------- frontend/src/components/Ticket/index.js | 12 +- .../components/TicketActionButtons/index.js | 6 +- 11 files changed, 297 insertions(+), 190 deletions(-) create mode 100644 backend/src/controllers/SchedulingNotifyController.ts create mode 100644 backend/src/routes/SchedulingNotifyRoutes.ts diff --git a/backend/src/controllers/SchedulingNotifyController.ts b/backend/src/controllers/SchedulingNotifyController.ts new file mode 100644 index 0000000..4185686 --- /dev/null +++ b/backend/src/controllers/SchedulingNotifyController.ts @@ -0,0 +1,15 @@ +import { Request, Response } from "express"; + +import DeleteSchedulingNotifyService from "../services/SchedulingNotifyServices/DeleteSchedulingNotifyService"; + + +export const remove = async ( req: Request, res: Response ): Promise => { + + console.log('EEEEEEEEEEEEEEEEEEEEEEEEEEE') + + const { scheduleId } = req.params; + + await DeleteSchedulingNotifyService(scheduleId); + + return res.status(200).send(); +}; diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index 3b05732..639de72 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -148,6 +148,7 @@ export const update = async ( req: Request, res: Response ): Promise = ticketId: scheduleData.ticketId, scheduleId: scheduleData.scheduleId, schedulingDate: scheduleData.schedulingDate, + schedulingTime: scheduleData.schedulingTime, message: scheduleData.message } ) diff --git a/backend/src/database/migrations/20220224003939-create-scheduling-notify.ts b/backend/src/database/migrations/20220224003939-create-scheduling-notify.ts index 765d4d1..1d65dd1 100644 --- a/backend/src/database/migrations/20220224003939-create-scheduling-notify.ts +++ b/backend/src/database/migrations/20220224003939-create-scheduling-notify.ts @@ -27,6 +27,10 @@ module.exports = { type: DataTypes.DATE, allowNull: false }, + schedulingTime: { + type: DataTypes.DATE, + allowNull: false + }, message: { type: DataTypes.STRING, allowNull: false diff --git a/backend/src/models/SchedulingNotify.ts b/backend/src/models/SchedulingNotify.ts index c8eff6d..af94607 100644 --- a/backend/src/models/SchedulingNotify.ts +++ b/backend/src/models/SchedulingNotify.ts @@ -38,6 +38,9 @@ import { @Column schedulingDate: Date; + @Column + schedulingTime: Date; + @Column message: string diff --git a/backend/src/routes/SchedulingNotifyRoutes.ts b/backend/src/routes/SchedulingNotifyRoutes.ts new file mode 100644 index 0000000..975b5a1 --- /dev/null +++ b/backend/src/routes/SchedulingNotifyRoutes.ts @@ -0,0 +1,10 @@ +import { Router } from "express"; +import isAuth from "../middleware/isAuth"; + +import * as SchedulingNotifyController from "../controllers/SchedulingNotifyController"; + +const schedulingNotifiyRoutes = Router(); + +schedulingNotifiyRoutes.delete("/schedule/:scheduleId", isAuth, SchedulingNotifyController.remove); + +export default schedulingNotifiyRoutes; diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index ab2d36e..98bcf6b 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -11,6 +11,7 @@ import whatsappSessionRoutes from "./whatsappSessionRoutes"; import queueRoutes from "./queueRoutes"; import quickAnswerRoutes from "./quickAnswerRoutes"; import reportRoutes from "./reportRoutes"; +import schedulingNotifiyRoutes from "./SchedulingNotifyRoutes"; const routes = Router(); @@ -26,6 +27,7 @@ routes.use(whatsappSessionRoutes); routes.use(queueRoutes); routes.use(quickAnswerRoutes); +routes.use(schedulingNotifiyRoutes) routes.use(reportRoutes); diff --git a/backend/src/services/SchedulingNotifyServices/CreateSchedulingNotifyService.ts b/backend/src/services/SchedulingNotifyServices/CreateSchedulingNotifyService.ts index 8f6252e..70ec9e1 100644 --- a/backend/src/services/SchedulingNotifyServices/CreateSchedulingNotifyService.ts +++ b/backend/src/services/SchedulingNotifyServices/CreateSchedulingNotifyService.ts @@ -6,6 +6,7 @@ interface Request { ticketId: string, scheduleId: string, schedulingDate: string, + schedulingTime: string, message: string } @@ -15,6 +16,7 @@ const CreateSchedulingNotifyService = async ( ticketId, scheduleId, schedulingDate, + schedulingTime, message }: Request): Promise => { @@ -24,6 +26,7 @@ const CreateSchedulingNotifyService = async ( ticketId, scheduleId, schedulingDate, + schedulingTime, message }) diff --git a/backend/src/services/SchedulingNotifyServices/ListSchedulingNotifyContactService.ts b/backend/src/services/SchedulingNotifyServices/ListSchedulingNotifyContactService.ts index 5313116..2a20ee0 100644 --- a/backend/src/services/SchedulingNotifyServices/ListSchedulingNotifyContactService.ts +++ b/backend/src/services/SchedulingNotifyServices/ListSchedulingNotifyContactService.ts @@ -9,7 +9,8 @@ const ListSchedulingNotifyContactService = async (contactNumber: string): Promis const ticket = await SchedulingNotify.findAll({ - attributes:['id', [Sequelize.fn("DATE_FORMAT",Sequelize.col("schedulingDate"),"%d/%m/%Y %H:%i:%s"),"schedulingDate"], 'message'], + attributes:['id', [Sequelize.fn("DATE_FORMAT",Sequelize.col("schedulingDate"),"%d/%m/%Y %H:%i:%s"),"schedulingDate"], + [Sequelize.fn("DATE_FORMAT",Sequelize.col("schedulingTime"),"%d/%m/%Y %H:%i:%s"),"schedulingTime"], 'message'], include: [ { diff --git a/frontend/src/components/ChatEnd/ModalChatEnd/index.js b/frontend/src/components/ChatEnd/ModalChatEnd/index.js index d220238..b2346fe 100644 --- a/frontend/src/components/ChatEnd/ModalChatEnd/index.js +++ b/frontend/src/components/ChatEnd/ModalChatEnd/index.js @@ -1,59 +1,73 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef, useReducer } from 'react'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; - +import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; import PropTypes from 'prop-types'; import Box from '@mui/material/Box'; -import SelectField from "../../Report/SelectField"; - +import SelectField from "../../Report/SelectField"; import TextFieldSelectHourBefore from '@mui/material/TextField'; -import MenuItem from '@mui/material/MenuItem'; - +import MenuItem from '@mui/material/MenuItem'; import DatePicker from '../../Report/DatePicker' -import TimerPickerSelect from '../TimerPickerSelect' - -// import MainHeader from "../../components/MainHeader"; -// import MainHeaderButtonsWrapper from "../../components/MainHeaderButtonsWrapper"; -// import TableRowSkeleton from "../../components/TableRowSkeleton"; -// import Title from "../../components/Title"; - - -import TextareaAutosize from '@mui/material/TextareaAutosize'; - -import { addHours, subHours, subMinutes } from "date-fns"; - +import TimerPickerSelect from '../TimerPickerSelect' +import TextareaAutosize from '@mui/material/TextareaAutosize'; +import { subHours } from "date-fns"; + import { - IconButton, - makeStyles, + IconButton, Paper, Table, TableBody, TableCell, TableHead, - TableRow, - Typography, -} from "@material-ui/core"; + TableRow, +} from "@material-ui/core"; + +import { DeleteOutline } from "@material-ui/icons"; +import { toast } from "react-toastify"; import api from "../../../services/api"; +import toastError from "../../../errors/toastError"; +import ConfirmationModal from "../../ConfirmationModal"; -const useStyles = makeStyles((theme) => ({ - mainPaper: { - flex: 1, - padding: theme.spacing(1), - overflowY: "scroll", - ...theme.scrollbarStyles, - }, - customTableCell: { - display: "flex", - alignItems: "center", - justifyContent: "center", - }, -})); + +const reducer = (state, action) => { + + + if (action.type === "LOAD_SCHEDULES") { + const schedulesContact = action.payload; + const newSchedules= []; + + schedulesContact.forEach((schedule) => { + const scheduleIndex = state.findIndex((s) => s.id === schedule.id); + if (scheduleIndex !== -1) { + state[scheduleIndex] = schedule; + } else { + newSchedules.push(schedule); + } + }); + + return [...state, ...newSchedules]; + } + + if (action.type === "DELETE_SCHEDULE") { + const scheduleId = action.payload; + const scheduleIndex = state.findIndex((q) => q.id === scheduleId); + if (scheduleIndex !== -1) { + state.splice(scheduleIndex, 1); + } + return [...state]; + } + + if (action.type === "RESET") { + return []; + } +}; + + const Item = (props) => { @@ -90,9 +104,11 @@ Item.propTypes = { -const Modal = (props) => { +const Modal = (props) => { - const classes = useStyles(); + // const [clientSchedules, dispatch] = useReducer(reducer, []); + const [selectedSchedule, setSelectedSchedule] = useState(null); + const [confirmModalOpen, setConfirmModalOpen] = useState(false); const [open, setOpen] = useState(true); const [scroll, /*setScroll*/] = useState('body'); @@ -104,11 +120,10 @@ const Modal = (props) => { const [data] = useState(props.schedules) - const [schedulesContact] = useState(props.schedulesContact) + const [schedulesContact, dispatch] = useReducer(reducer, []); - const [currencyHourBefore, setCurrency] = useState(formatedTimeHour(subHours(timerPicker,1))); - const [currenciesTimeBefore, setCurrenciesTimeBefore] = useState( ); - const [test, setTest] = useState(null); + const [currencyHourBefore, setCurrency] = useState(null); + const [currenciesTimeBefore, setCurrenciesTimeBefore] = useState( ); const handleCancel = (event, reason) => { @@ -119,6 +134,22 @@ const Modal = (props) => { }; + + useEffect(() => { + + (async () => { + try { + + const { data } = await api.get("/tickets/" + props.ticketId); + + dispatch({ type: "LOAD_SCHEDULES", payload: data.schedulesContact }); + + } catch (err) { + toastError(err); + } + })(); + }, [props]); + function greetMessageSchedule(scheduleDate){ @@ -129,35 +160,84 @@ const Modal = (props) => { return `${timer.getHours().toString().padStart(2, '0')}:${timer.getMinutes().toString().padStart(2, '0')}` } - function hoursBeforeAvalible(timer){ - + const hoursBeforeAvalible = (timer) =>{ let hours = [] - let hour = 1 + let hour = 1 - while(subHours(timer, hour).getHours()>=6 /*&& subHours(timer, hour).getHours()>=new Date().getHours()*/){ + if(startDate === dateCurrentFormated()){ - console.log('******** TIMER: ', formatedTimeHour(subHours(timer,hour))) + console.log('HOJE++++') - hours.push( - {value: formatedTimeHour(subHours(timer,hour)), - label: `${hour} HORA ANTES DO HORÁRIO DO AGENDAMENTO`}) + while(subHours(timer, hour).getHours()>=6 && + subHours(timer, hour).getHours()>=new Date().getHours() && + subHours(timer, hour).getHours()<=19){ + + console.log('******** TIMER: ', formatedTimeHour(subHours(timer,hour))) + + hours.push( + {value: formatedTimeHour(subHours(timer,hour)), + label: `${hour} HORA ANTES DO HORÁRIO DO AGENDAMENTO`}) + + hour++; + } + + if(hours.length>1){ + console.log('entrou----------------------: ', hours.length) + hours.pop() + setCurrency(hours[0].value) + } + else{ + setCurrency(null) + } - hour++; } + else{ + + while(subHours(timer, hour).getHours()>=6 && subHours(timer, hour).getHours()<=19){ + + console.log('******** another day TIMER: ', formatedTimeHour(subHours(timer,hour))) + + hours.push( + {value: formatedTimeHour(subHours(timer,hour)), + label: `${hour} HORA ANTES DO HORÁRIO DO AGENDAMENTO`}) + + hour++; + } + + if(hours.length>0){ + console.log('entrou----------------------: ', hours.length) + setCurrency(hours[0].value) + } + else{ + setCurrency(null) + } - if(hours){ - setCurrency(hours[0].value) } - + - return hours - - console.log('HOURS: ',hours) + return {time: hours, hour:hour} } - + + + const handleCloseConfirmationModal = () => { + setConfirmModalOpen(false); + setSelectedSchedule(null); + }; + + + const handleDeleteSchedule = async (scheduleId) => { + try { + await api.delete(`/schedule/${scheduleId}`); + toast.success(("Agendamento deletado com sucesso!")); + dispatch({ type: "DELETE_SCHEDULE", payload: scheduleId }); + } catch (err) { + toastError(err); + } + setSelectedSchedule(null); + }; // Get from child 2 const datePickerValue = (data) => { @@ -190,7 +270,7 @@ const dateCurrentFormated = () => { if (scheduleId === '1'){ } - else if(textArea1.trim().length<10){ + else if(textArea1 && textArea1.trim().length<10){ alert('Mensagem muito curta!\nMínimo 10 caracteres.') return } @@ -199,6 +279,13 @@ const dateCurrentFormated = () => { alert('Horário comercial inválido!\n Selecione um horário de lembrete válido entre às 07:00 e 20:00') return } + else if(!currencyHourBefore){ + + alert('Para agendamentos do dia corrente, essa funcionalidade atende a agendeamentos com no mínimo 2 horas adiantado a partir da hora atual!') + + return + + } // else if(startDate === dateCurrentFormated()){ // if( // (new Date(subHours(timerPicker, 1)).getHours() <= new Date().getHours() && @@ -225,6 +312,7 @@ const dateCurrentFormated = () => { // 'schedulingDate': startDate+' '+formatedTimeHour(subHours(new Date(`${startDate} ${timerPicker.getHours()}:${timerPicker.getMinutes()}:${timerPicker.getSeconds()}`), 1))+':00', // 'schedulingDate': `${startDate} ${timerPicker.getHours()}:${timerPicker.getMinutes()}:${timerPicker.getSeconds()}`, 'schedulingDate': `${startDate} ${currencyHourBefore}:00`, + 'schedulingTime': startDate+' '+formatedTimeHour(new Date(`${startDate} ${timerPicker.getHours()}:${timerPicker.getMinutes()}:00`)), 'message': textArea1 }); @@ -255,7 +343,8 @@ const handleChangeHourBefore = (event) => { console.log('textFihandleChangeHourBefore: ',event.target.value); - setCurrency(event.target.value); + setCurrency(event.target.value); + }; @@ -271,24 +360,29 @@ const handleChangeHourBefore = (event) => { useEffect(()=>{ - if (parseInt(timerPicker.getHours()) > 11 && parseInt(timerPicker.getHours()) < 18){ - // setTextArea1('Boa tarde, '+greetMessageSchedule( formatedTimeHour(addHours(new Date(timerPicker), 1)))) + setCurrenciesTimeBefore(hoursBeforeAvalible(timerPicker).time) + +},[timerPicker, startDate]) + + +useEffect(()=>{ + + console.log('CURRENCY HOUR BEFORE: ', `${startDate} ${currencyHourBefore}:00`) + + let auxDate = new Date(`${startDate} ${currencyHourBefore}:00`) + + if (parseInt(auxDate.getHours()) > 11 && parseInt(auxDate.getHours()) < 18){ + setTextArea1('Boa tarde, '+greetMessageSchedule( formatedTimeHour(new Date(timerPicker), 1))) } - else if(parseInt(timerPicker.getHours()) < 12){ - // setTextArea1('Bom dia, '+greetMessageSchedule( formatedTimeHour(addHours(new Date(timerPicker), 1)))) + else if(parseInt(auxDate.getHours()) < 12){ setTextArea1('Bom dia, '+greetMessageSchedule( formatedTimeHour(new Date(timerPicker), 1))) } - else if(parseInt(timerPicker.getHours()) > 17){ - - // setTextArea1('Boa noite, '+greetMessageSchedule( formatedTimeHour(addHours(new Date(timerPicker), 1)))) + else if(parseInt(auxDate.getHours()) > 17){ setTextArea1('Boa noite, '+greetMessageSchedule( formatedTimeHour(new Date(timerPicker), 1))) - } - - setCurrenciesTimeBefore(hoursBeforeAvalible(timerPicker)) - - -},[timerPicker]) + } + +},[currencyHourBefore, startDate]) const handleChange = (event) => { @@ -300,6 +394,8 @@ const handleChange = (event) => { return ( + + { - - - - {currenciesTimeBefore.map((option) => ( - - {option.label} - - ))} - - + + {currencyHourBefore && + + + {currenciesTimeBefore.map((option) => ( + + {option.label} + + ))} + + + } { - } + } - {/* - - Titulo 1 - - - - - - - - - - table name - - - table color - - - table greeting - - - table actions - - - - - <> - {schedulesContact.map((queue) => ( - - {queue.name} - -
- -
-
- -
- - {queue.greetingMessage} - -
-
- - handleEditQueue(queue)} - > - - - - { - setSelectedQueue(queue); - setConfirmModalOpen(true); - }} - > - - - -
- ))} - {loading && } - -
-
-
+ {schedulesContact.length>0 && -
*/} + + + + + handleDeleteSchedule(selectedSchedule.id)} + > + Deseja realmente deletar esse Agendamento? + + Agendamentos + + + + + + + Data + + + Hora + + + Deletar + + + + + + <> + {schedulesContact.map((scheduleData, index) => ( + + {scheduleData.schedulingDate.split(' ')[0]} + {scheduleData.schedulingTime.split(' ')[1]} + + + { + setSelectedSchedule(scheduleData); + setConfirmModalOpen(true); + + }} + > + + + + + + ))} + + +
+
+
} + diff --git a/frontend/src/components/Ticket/index.js b/frontend/src/components/Ticket/index.js index da72e66..fe0dd8f 100644 --- a/frontend/src/components/Ticket/index.js +++ b/frontend/src/components/Ticket/index.js @@ -83,8 +83,7 @@ const Ticket = () => { const [contact, setContact] = useState({}); const [ticket, setTicket] = useState({}); - const [schedule, setSchedule] = useState({}) - const [schedulesContact, setSchedulesContact] = useState({}) + const [schedule, setSchedule] = useState({}) useEffect(() => { setLoading(true); @@ -97,15 +96,12 @@ const Ticket = () => { const { data } = await api.get("/tickets/" + ticketId); // setContact(data.contact); - // setTicket(data); - - console.log('SCHEDULE CONTACT: ',data.schedulesContact) + // setTicket(data); setContact(data.contact.contact); setTicket(data.contact); - setSchedule(data.schedules) - setSchedulesContact(data.schedulesContact) + setSchedule(data.schedules) setLoading(false); } catch (err) { @@ -176,7 +172,7 @@ const Ticket = () => { />
- +
diff --git a/frontend/src/components/TicketActionButtons/index.js b/frontend/src/components/TicketActionButtons/index.js index d2a1ad0..cb01b61 100644 --- a/frontend/src/components/TicketActionButtons/index.js +++ b/frontend/src/components/TicketActionButtons/index.js @@ -27,7 +27,7 @@ const useStyles = makeStyles(theme => ({ }, })); -const TicketActionButtons = ({ ticket, schedule, schedulesContact }) => { +const TicketActionButtons = ({ ticket, schedule }) => { const classes = useStyles(); const history = useHistory(); const [anchorEl, setAnchorEl] = useState(null); @@ -64,8 +64,8 @@ const TicketActionButtons = ({ ticket, schedule, schedulesContact }) => { render() };