Codificação para criação do agendamento no backend

pull/1/head
adriano 2022-02-28 10:51:11 -03:00
parent 2668eaaf3d
commit d0918ac2a3
19 changed files with 631 additions and 54 deletions

View File

@ -0,0 +1,38 @@
import { Request, Response } from "express";
// import { getIO } from "../libs/socket";
import CreateSchedulingNotifyService from "../services/SchedulingNotifyServices/CreateSchedulingNotifyService";
interface SchedulingNotifyData {
schedulingId: number | string;
ticketId: number | string;
cpf_cnpj: string;
schedulingDate: string;
reminder: string;
message: string;
status: string;
}
export const store = async (req: Request, res: Response): Promise<Response> => {
const { schedulingId, ticketId, cpf_cnpj, schedulingDate, reminder, message, status}: SchedulingNotifyData = req.body;
const schedulingNotify = await CreateSchedulingNotifyService({
schedulingId,
ticketId,
cpf_cnpj,
schedulingDate,
reminder,
message,
status
}
)
// const io = getIO();
// io.to(ticket.status).emit("ticket", {
// action: "update",
// ticket
// });
return res.status(200).json(schedulingNotify);
};

View File

@ -11,6 +11,8 @@ import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService
type IndexQuery = { type IndexQuery = {
searchParam: string; searchParam: string;
pageNumber: string; pageNumber: string;
@ -29,6 +31,7 @@ interface TicketData {
} }
import ListScheduleService from "../services/ScheduleService/ListScheduleService";
export const index = async (req: Request, res: Response): Promise<Response> => { export const index = async (req: Request, res: Response): Promise<Response> => {
const { const {
@ -83,18 +86,22 @@ export const show = async (req: Request, res: Response): Promise<Response> => {
const contact = await ShowTicketService(ticketId); const contact = await ShowTicketService(ticketId);
//console.log('------- contact: ',JSON.stringify(contact)) const { schedules, count, hasMore } = await ListScheduleService({ searchParam: "", pageNumber: "1" });
return res.status(200).json(contact); // return res.status(200).json(contact);
return res.status(200).json({contact, schedules});
}; };
export const update = async ( export const update = async ( req: Request, res: Response): Promise<Response> => {
req: Request,
res: Response
): Promise<Response> => {
const { ticketId } = req.params; const { ticketId } = req.params;
const ticketData: TicketData = req.body; const ticketData: TicketData = req.body;
// const { contactId, status, queueId, userId, schedulingNotify} = req.body
const { ticket } = await UpdateTicketService({ const { ticket } = await UpdateTicketService({
ticketData, ticketData,
ticketId ticketId
@ -110,7 +117,6 @@ export const update = async (
} }
} }
return res.status(200).json(ticket); return res.status(200).json(ticket);
}; };

View File

@ -11,6 +11,10 @@ import WhatsappQueue from "../models/WhatsappQueue";
import UserQueue from "../models/UserQueue"; import UserQueue from "../models/UserQueue";
import QuickAnswer from "../models/QuickAnswer"; import QuickAnswer from "../models/QuickAnswer";
import SchedulingNotify from "../models/SchedulingNotify";
import Schedule from "../models/Schedule";
// eslint-disable-next-line // eslint-disable-next-line
const dbConfig = require("../config/database"); const dbConfig = require("../config/database");
// import dbConfig from "../config/database"; // import dbConfig from "../config/database";
@ -28,7 +32,10 @@ const models = [
Queue, Queue,
WhatsappQueue, WhatsappQueue,
UserQueue, UserQueue,
QuickAnswer QuickAnswer,
SchedulingNotify,
Schedule,
]; ];
sequelize.addModels(models); sequelize.addModels(models);

View File

@ -0,0 +1,30 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.createTable("Schedules", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false
},
name: {
type: DataTypes.STRING,
allowNull: false
},
createdAt: {
type: DataTypes.DATE,
allowNull: false
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false
}
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.dropTable("Schedules");
}
};

View File

@ -0,0 +1,61 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.createTable("SchedulingNotifies", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false
},
schedulingId: {
type: DataTypes.INTEGER,
references: { model: "Schedules", key: "id" },
onUpdate: "CASCADE",
onDelete: "CASCADE",
allowNull: false
},
ticketId: {
type: DataTypes.INTEGER,
references: { model: "Tickets", key: "id" },
onUpdate: "CASCADE",
onDelete: "CASCADE",
allowNull: false
},
cpf_cnpj: {
type: DataTypes.STRING,
allowNull: true
},
schedulingDate: {
type: DataTypes.DATE,
allowNull: false
},
reminder: {
type: DataTypes.STRING,
allowNull: true
},
message: {
type: DataTypes.STRING,
allowNull: false
},
status: {
type: DataTypes.STRING,
allowNull: true
},
createdAt: {
type: DataTypes.DATE,
allowNull: false
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false
}
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.dropTable("SchedulingNotifies");
}
};

View File

@ -0,0 +1,36 @@
import { QueryInterface } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.bulkInsert(
"Schedules",
[
{
name: "DÚVIDAS",
createdAt: new Date(),
updatedAt: new Date()
},
{
name: "AGENDAMENTO À CONFIRMAR",
createdAt: new Date(),
updatedAt: new Date()
},
{
name: "SEM RETORNO DO CLIENTE",
createdAt: new Date(),
updatedAt: new Date()
},
{
name: "AGENDAMENTO CONFIRMADO",
createdAt: new Date(),
updatedAt: new Date()
}
],
{}
);
},
down: (queryInterface: QueryInterface) => {
return queryInterface.bulkDelete("Schedules", {});
}
};

View File

@ -0,0 +1,35 @@
import {
Table,
AutoIncrement,
Column,
CreatedAt,
UpdatedAt,
Model,
PrimaryKey,
HasMany
} from "sequelize-typescript";
import SchedulingNotify from "./SchedulingNotify";
@Table
class Schedule extends Model<Schedule> {
@PrimaryKey
@AutoIncrement
@Column
id: number;
@Column
name: string;
@CreatedAt
createdAt: Date;
@UpdatedAt
updatedAt: Date;
@HasMany(() => SchedulingNotify)
SchedulingNotifies: SchedulingNotify[];
}
export default Schedule;

View File

@ -0,0 +1,62 @@
import {
Table,
AutoIncrement,
Column,
CreatedAt,
UpdatedAt,
Model,
PrimaryKey,
ForeignKey,
BelongsTo
} from "sequelize-typescript";
import Schedule from "./Schedule";
import Ticket from "./Ticket";
@Table
class SchedulingNotify extends Model<SchedulingNotify> {
@PrimaryKey
@AutoIncrement
@Column
id: number;
@ForeignKey(() => Schedule)
@Column
scheduleId: number;
@BelongsTo(() => Schedule)
schedule: Schedule;
@ForeignKey(() => Ticket)
@Column
ticketId: number;
@BelongsTo(() => Ticket)
ticket: Ticket;
@Column
cpf_cnpj: string;
@Column
schedulingDate: Date;
@Column
reminder: string
@Column
message: string
@Column
status: string
@CreatedAt
createdAt: Date;
@UpdatedAt
updatedAt: Date;
}
export default SchedulingNotify;

View File

@ -8,6 +8,7 @@ import {
ForeignKey, ForeignKey,
BelongsTo, BelongsTo,
HasMany, HasMany,
HasOne,
AutoIncrement, AutoIncrement,
Default Default
} from "sequelize-typescript"; } from "sequelize-typescript";
@ -18,6 +19,8 @@ import Queue from "./Queue";
import User from "./User"; import User from "./User";
import Whatsapp from "./Whatsapp"; import Whatsapp from "./Whatsapp";
import SchedulingNotify from "./SchedulingNotify";
@Table @Table
class Ticket extends Model<Ticket> { class Ticket extends Model<Ticket> {
@PrimaryKey @PrimaryKey
@ -74,6 +77,9 @@ class Ticket extends Model<Ticket> {
@HasMany(() => Message) @HasMany(() => Message)
messages: Message[]; messages: Message[];
@HasOne(() => SchedulingNotify)
schedulingNotify: SchedulingNotify
} }
export default Ticket; export default Ticket;

View File

@ -0,0 +1,44 @@
import { Sequelize } from "sequelize";
import Schedule from "../../models/Schedule";
interface Request {
searchParam?: string;
pageNumber?: string;
}
interface Response {
schedules: Schedule[];
count: number;
hasMore: boolean;
}
const ListSchedulingService = async ({
searchParam = "",
pageNumber = "1"
}: Request): Promise<Response> => {
const whereCondition = {
message: Sequelize.where(
Sequelize.fn("LOWER", Sequelize.col("name")), "LIKE", `%${searchParam.toLowerCase().trim()}%`)
};
const limit = 20;
const offset = limit * (+pageNumber - 1);
const { count, rows: schedules } = await Schedule.findAndCountAll({
where: whereCondition,
attributes: ['id', 'name'],
limit,
offset,
order: [["id", "ASC"]]
});
const hasMore = count > offset + schedules.length;
return {
schedules,
count,
hasMore
};
};
export default ListSchedulingService;

View File

@ -0,0 +1,41 @@
import AppError from "../../errors/AppError";
import SchedulingNotify from "../../models/SchedulingNotify";
interface Request {
schedulingId: number | string,
ticketId: number | string,
cpf_cnpj: string,
schedulingDate: string,
reminder: string,
message: string,
status: string
}
const CreateSchedulingNotifyService = async ({
schedulingId,
ticketId,
cpf_cnpj,
schedulingDate,
reminder,
message,
status
}: Request): Promise<SchedulingNotify> => {
const schedulingNotify = await SchedulingNotify.create(
{
schedulingId,
ticketId,
cpf_cnpj,
schedulingDate,
reminder,
message,
status
}
)
return schedulingNotify
}
export default CreateSchedulingNotifyService

View File

@ -0,0 +1,17 @@
import AppError from "../../errors/AppError";
import SchedulingNotify from "../../models/SchedulingNotify";
const DeleteSchedulingNotifyService = async (id: string | number): Promise<void> => {
const schedulingNotify = await SchedulingNotify.findOne({
where: { id }
});
if (!schedulingNotify) {
throw new AppError("ERR_NO_SCHEDULING_NOTIFY_FOUND", 404);
}
await schedulingNotify.destroy();
};
export default DeleteSchedulingNotifyService;

View File

@ -0,0 +1,43 @@
import { Sequelize } from "sequelize";
import SchedulingNotify from "../../models/SchedulingNotify";
interface Request {
searchParam?: string;
pageNumber?: string;
}
interface Response {
schedulingNotifies: SchedulingNotify[];
count: number;
hasMore: boolean;
}
const ListSchedulingNotifyService = async ({
searchParam = "",
pageNumber = "1"
}: Request): Promise<Response> => {
const whereCondition = {
message: Sequelize.where(
Sequelize.fn("LOWER", Sequelize.col("cpf_cnpj")), "LIKE", `%${searchParam.toLowerCase().trim()}%`)
};
const limit = 20;
const offset = limit * (+pageNumber - 1);
const { count, rows: schedulingNotifies } = await SchedulingNotify.findAndCountAll({
where: whereCondition,
limit,
offset,
order: [["cpf_cnpj", "ASC"]]
});
const hasMore = count > offset + schedulingNotifies.length;
return {
schedulingNotifies,
count,
hasMore
};
};
export default ListSchedulingNotifyService;

View File

@ -0,0 +1,15 @@
import AppError from "../../errors/AppError";
import SchedulingNotify from "../../models/SchedulingNotify";
const ShowSchedulingNotifyService = async (id: string): Promise<SchedulingNotify> => {
const schedulingNotify = await SchedulingNotify.findByPk(id);
if (!schedulingNotify) {
throw new AppError("ERR_NO_SCHEDULING_NOTIFY_FOUND", 404);
}
return schedulingNotify;
};
export default ShowSchedulingNotifyService;

View File

@ -0,0 +1,39 @@
import AppError from "../../errors/AppError";
import SchedulingNotify from "../../models/SchedulingNotify";
interface SchedulingData {
name?: string
}
interface Request {
schedulingData: SchedulingData
schedulingDataId: string
}
const UpdateSchedulingNotify = async ({
schedulingData,
schedulingDataId
}: Request): Promise<SchedulingNotify> => {
const { name } = schedulingData;
const updateScheduling = await SchedulingNotify.findOne({
where: { id: schedulingDataId },
attributes: ["id", "name"]
});
if (!updateScheduling) {
//console.log('NOT FOUND SCHEDULING NOTIFY')
throw new AppError("ERR_NO_SCHEDULING_NOTIFY_FOUND", 404);
}
await updateScheduling.update({ name });
await updateScheduling.reload({
attributes: ["id", "name"]
});
return updateScheduling;
};
export default UpdateSchedulingNotify;

View File

@ -65,29 +65,44 @@ const Modal = (props) => {
const [textArea2, setTextArea2] = useState() const [textArea2, setTextArea2] = useState()
const [textFieldCpfCnpj, setTextField] = useState() const [textFieldCpfCnpj, setTextField] = useState()
const [data] = useState([ const [data] = useState(props.schedules)
{'id': 1, 'name': 'STATUS1'}, const [chatEnd, setChatEnd] = useState(false)
{'id': 2, 'name': 'STATUS2'},
{'id': 3, 'name': 'STATUS3'},
{'id': 4, 'name': 'STATUS4'},
])
const handleClose = (event, reason) => { const handleCancel = (event, reason) => {
if (reason && reason === "backdropClick") if (reason && reason === "backdropClick")
return; return;
setChatEnd(null)
setOpen(false); setOpen(false);
}; };
const handleChatEnd = (event, reason) => { const handleChatEnd = (event, reason) => {
if (reason && reason === "backdropClick") if (reason && reason === "backdropClick")
return; return;
setChatEnd({
'schedulingId': schedulingId,
'cpf_cnpj': textFieldCpfCnpj,
'schedulingDate': `${startDate} ${timerPicker.getHours()}:${timerPicker.getMinutes()}:${timerPicker.getSeconds()}`,
'reminder': textArea1,
'message': textArea2,
'status': ''
})
setOpen(false); setOpen(false);
}; };
useEffect(()=>{
props.func(chatEnd);
}, [chatEnd])
const descriptionElementRef = useRef(null); const descriptionElementRef = useRef(null);
useEffect(() => { useEffect(() => {
@ -143,7 +158,7 @@ const handleTextFieldChange = (event) => {
<Dialog <Dialog
open={open} open={open}
onClose={handleClose} onClose={handleCancel}
// fullWidth={true} // fullWidth={true}
// maxWidth={true} // maxWidth={true}
disableEscapeKeyDown disableEscapeKeyDown
@ -236,7 +251,7 @@ const handleTextFieldChange = (event) => {
<DialogActions> <DialogActions>
<div style={{marginRight:'50px'}}> <div style={{marginRight:'50px'}}>
<Button onClick={handleClose}>Cancelar</Button> <Button onClick={handleCancel}>Cancelar</Button>
</div> </div>
<Button onClick={handleChatEnd}>Ok</Button> <Button onClick={handleChatEnd}>Ok</Button>
</DialogActions> </DialogActions>

View File

@ -1,7 +1,41 @@
// import React, { useState } from "react";
// import { DateTimePicker, KeyboardDateTimePicker } from "@material-ui/pickers";
// function InlineDateTimePickerDemo(props) {
// const [selectedDate, handleDateChange] = useState(new Date("2018-01-01T00:00:00.000Z"));
// return (
// <>
// <DateTimePicker
// variant="inline"
// label="Basic example"
// value={selectedDate}
// onChange={handleDateChange}
// />
// <KeyboardDateTimePicker
// variant="inline"
// ampm={false}
// label="With keyboard"
// value={selectedDate}
// onChange={handleDateChange}
// onError={console.log}
// disablePast
// format="yyyy/MM/dd HH:mm"
// />
// </>
// );
// }
// export default InlineDateTimePickerDemo;
import React, { Fragment, useState, useEffect } from "react"; import React, { Fragment, useState, useEffect } from "react";
import TextField from '@mui/material/TextField'; // import TextField from '@mui/material/TextField';
import DateFnsUtils from '@date-io/date-fns'; import DateFnsUtils from '@date-io/date-fns';
import { import {
@ -31,9 +65,10 @@ const ResponsiveTimePickers = (props) => {
<MuiPickersUtilsProvider utils={DateFnsUtils} locale={ptBrLocale}> <MuiPickersUtilsProvider utils={DateFnsUtils} locale={ptBrLocale}>
<TimePicker <TimePicker
variant="outline"
label="Hora do lembrete" label="Hora do lembrete"
value={value} value={value}
ampm={false}
onChange={(newValue) => { onChange={(newValue) => {
setValue(newValue); setValue(newValue);
}} }}

View File

@ -83,17 +83,26 @@ const Ticket = () => {
const [contact, setContact] = useState({}); const [contact, setContact] = useState({});
const [ticket, setTicket] = useState({}); const [ticket, setTicket] = useState({});
const [schedule, setSchedule] = useState({})
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
const fetchTicket = async () => { const fetchTicket = async () => {
try { try {
// maria julia
const { data } = await api.get("/tickets/" + ticketId); const { data } = await api.get("/tickets/" + ticketId);
setContact(data.contact); // setContact(data.contact);
setTicket(data); // setTicket(data);
setContact(data.contact.contact);
setTicket(data.contact);
setSchedule(data.schedules)
setLoading(false); setLoading(false);
} catch (err) { } catch (err) {
setLoading(false); setLoading(false);
@ -163,7 +172,7 @@ const Ticket = () => {
/> />
</div> </div>
<div className={classes.ticketActionButtons}> <div className={classes.ticketActionButtons}>
<TicketActionButtons ticket={ticket} /> <TicketActionButtons ticket={ticket} schedule={schedule}/>
</div> </div>
</TicketHeader> </TicketHeader>
<ReplyMessageProvider> <ReplyMessageProvider>

View File

@ -1,4 +1,4 @@
import React, { useContext, useState } from "react"; import React, { useContext, useState, useEffect } from "react";
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";
@ -27,7 +27,7 @@ const useStyles = makeStyles(theme => ({
}, },
})); }));
const TicketActionButtons = ({ ticket }) => { const TicketActionButtons = ({ ticket, schedule }) => {
const classes = useStyles(); const classes = useStyles();
const history = useHistory(); const history = useHistory();
const [anchorEl, setAnchorEl] = useState(null); const [anchorEl, setAnchorEl] = useState(null);
@ -35,6 +35,8 @@ const TicketActionButtons = ({ ticket }) => {
const ticketOptionsMenuOpen = Boolean(anchorEl); const ticketOptionsMenuOpen = Boolean(anchorEl);
const { user } = useContext(AuthContext); const { user } = useContext(AuthContext);
const [chatEnd, setChatEnd] = useState(null)
const handleOpenTicketOptionsMenu = e => { const handleOpenTicketOptionsMenu = e => {
setAnchorEl(e.currentTarget); setAnchorEl(e.currentTarget);
}; };
@ -43,36 +45,67 @@ const TicketActionButtons = ({ ticket }) => {
setAnchorEl(null); setAnchorEl(null);
}; };
const chatEndVal = (data) => {
if(data){
console.log('ChatEnd: ',(data));
// handleUpdateTicketStatus(e, "closed", user?.id)
}
setChatEnd(data)
}
const handleModal = (e, status, userId) => {
render(<Modal
modal_header={'Finalização de Atendimento'}
func={chatEndVal}
schedules={schedule}
status={status}
userId={userId}
ticketId={ticket.id}/>)
};
const handleUpdateTicketStatus = async (e, status, userId) => { const handleUpdateTicketStatus = async (e, status, userId) => {
// Thuanny // Thuanny
//alert(`ticket.id: ${ticket.id} | status: ${status}| userId: ${userId}`)
render(<Modal modal_header={'Finalização de Atendimento'}/>)
console.log('Constinuação..................') // render(<Modal
// modal_header={'Finalização de Atendimento'}
// func={chatEndVal} schedules={schedule}
// e={e}
// status={status}
// userId={userId}/>)
setLoading(true);
try {
await api.put(`/tickets/${ticket.id}`, {
status: status,
userId: userId || null,
});
setLoading(false);
if (status === "open") {
history.push(`/tickets/${ticket.id}`);
} else {
history.push("/tickets");
}
} catch (err) {
setLoading(false);
toastError(err);
}
// setLoading(true);
// try {
// await api.put(`/tickets/${ticket.id}`, {
// status: status,
// userId: userId || null,
// });
// setLoading(false);
// if (status === "open") {
// history.push(`/tickets/${ticket.id}`);
// } else {
// history.push("/tickets");
// }
// } catch (err) {
// setLoading(false);
// toastError(err);
// }
}; };
return ( return (
@ -103,7 +136,12 @@ const TicketActionButtons = ({ ticket }) => {
size="small" size="small"
variant="contained" variant="contained"
color="primary" color="primary"
onClick={e => handleUpdateTicketStatus(e, "closed", user?.id)} onClick={e => {
handleModal(e, "closed", user?.id)
// handleUpdateTicketStatus(e, "closed", user?.id)
}}
> >
{i18n.t("messagesList.header.buttons.resolve")} {i18n.t("messagesList.header.buttons.resolve")}
</ButtonWithSpinner> </ButtonWithSpinner>