Correção de conflito no merge

pull/1/head
adriano 2022-03-06 16:41:25 -03:00
commit b5ec63b02c
31 changed files with 1192 additions and 96 deletions

View File

@ -51,6 +51,9 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
}) })
); );
} else { } else {
console.log('------- quotedMsg: ', quotedMsg)
await SendWhatsAppMessage({ body, ticket, quotedMsg }); await SendWhatsAppMessage({ body, ticket, quotedMsg });
} }

View File

@ -9,6 +9,9 @@ import UpdateTicketService from "../services/TicketServices/UpdateTicketService"
import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage"; import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage";
import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService"; import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService";
import CreateSchedulingNotifyService from "../services/SchedulingNotifyServices/CreateSchedulingNotifyService";
type IndexQuery = { type IndexQuery = {
@ -29,6 +32,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 {
@ -66,6 +70,11 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
export const store = async (req: Request, res: Response): Promise<Response> => { export const store = async (req: Request, res: Response): Promise<Response> => {
const { contactId, status, userId }: TicketData = req.body; const { contactId, status, userId }: TicketData = req.body;
// naty
// console.log(
// `contactId: ${contactId} \n| status: ${status} \n| userId: ${userId}`
// )
const ticket = await CreateTicketService({ contactId, status, userId }); const ticket = await CreateTicketService({ contactId, status, userId });
const io = getIO(); const io = getIO();
@ -83,24 +92,36 @@ 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 (
req: Request,
res: Response
): Promise<Response> => {
export const update = async ( req: Request, res: Response ): Promise<Response> => {
const { ticketId } = req.params; const { ticketId } = req.params;
const ticketData: TicketData = req.body;
let ticket2 = {}
if(req.body['status'] === "closed"){
const {status, userId, schedulingNotifyData} = req.body;
const { ticket } = await UpdateTicketService({ const { ticket } = await UpdateTicketService({
ticketData, ticketData:{'status': status, 'userId': userId},
ticketId ticketId
}); });
if (ticket.status === "closed") {
///////////////////////////////
const whatsapp = await ShowWhatsAppService(ticket.whatsappId); const whatsapp = await ShowWhatsAppService(ticket.whatsappId);
const { farewellMessage } = whatsapp; const { farewellMessage } = whatsapp;
@ -108,12 +129,72 @@ export const update = async (
if (farewellMessage) { if (farewellMessage) {
await SendWhatsAppMessage({ body: farewellMessage, ticket }); await SendWhatsAppMessage({ body: farewellMessage, ticket });
} }
///////////////////////////////
// agendamento
const scheduleData = JSON.parse(schedulingNotifyData)
if( scheduleData.scheduleId != '1'){
const schedulingNotifyCreate = await CreateSchedulingNotifyService(
{
ticketId: scheduleData.ticketId,
scheduleId: scheduleData.scheduleId,
schedulingDate: scheduleData.schedulingDate,
message: scheduleData.message
}
)
} }
ticket2 = ticket
return res.status(200).json(ticket); }
else{
const ticketData: TicketData = req.body;
const { ticket } = await UpdateTicketService({
ticketData,
ticketId
});
ticket2 = ticket
}
return res.status(200).json(ticket2);
}; };
// export const update = async (
// req: Request,
// res: Response
// ): Promise<Response> => {
// const { ticketId } = req.params;
// const ticketData: TicketData = req.body;
// const { ticket } = await UpdateTicketService({
// ticketData,
// ticketId
// });
// if (ticket.status === "closed") {
// const whatsapp = await ShowWhatsAppService(ticket.whatsappId);
// const { farewellMessage } = whatsapp;
// if (farewellMessage) {
// await SendWhatsAppMessage({ body: farewellMessage, ticket });
// }
// }
// return res.status(200).json(ticket);
// };
export const remove = async ( export const remove = async (
req: Request, req: Request,
res: Response res: Response

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,48 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.createTable("SchedulingNotifies", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false
},
scheduleId: {
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
},
schedulingDate: {
type: DataTypes.DATE,
allowNull: false
},
message: {
type: DataTypes.STRING,
allowNull: false
},
createdAt: {
type: DataTypes.DATE,
allowNull: false
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false
}
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.dropTable("SchedulingNotifies");
}
};

View File

@ -6,10 +6,10 @@ module.exports = {
"Users", "Users",
[ [
{ {
name: "Administrador", name: "grupohit",
email: "admin@whaticket.com", email: "grupohit@communication.com",
passwordHash: "$2a$08$WaEmpmFDD/XkDqorkpQ42eUZozOqRCPkPcTkmHHMyuTGUOkI8dHsq", passwordHash: "$2a$08$98TKVkUCDr6ulrxIaRXFlup4U7CJtvHqK94I5pwvuh7VhOBKeL0pO",
profile: "admin", profile: "master",
tokenVersion: 0, tokenVersion: 0,
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date() updatedAt: new Date()

View File

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

View File

@ -0,0 +1,76 @@
import SetTicketMessagesAsRead from "../helpers/SetTicketMessagesAsRead";
import ShowTicketService from "../services/TicketServices/ShowTicketService";
import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage";
import ListSchedulingNotifyService from "../services/SchedulingNotifyServices/ListSchedulingNotifyService";
import DeleteSchedulingNotifyService from "../services/SchedulingNotifyServices/DeleteSchedulingNotifyService";
let countTest: number = 0
let scheduler_monitor:any;
// const test = async () => {
// const ticket = await ShowTicketService('336');
// SetTicketMessagesAsRead(ticket);
// await SendWhatsAppMessage({ body: 'Olá Sr Adriano, estamos entrando em contato para confirmar seu retorno hoje', ticket });
// }
const monitor = async () => {
countTest += 1
console.log(`${countTest}`)
let date = new Date()
let day = date.getDate().toString().padStart(2, '0');
let month = (date.getMonth()+1).toString().padStart(2, '0');
let year = date.getFullYear();
let hour = date.getHours()
let minute = date.getMinutes()
let fullDate = `${year}-${month}-${day}`;
let dateParm = `${fullDate} ${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:00`
console.log(dateParm);
const { schedulingNotifies, count, hasMore } = await ListSchedulingNotifyService({ searchParam: dateParm, pageNumber: "1" });
if(schedulingNotifies.length){
const ticket = await ShowTicketService(schedulingNotifies[0].ticketId);
SetTicketMessagesAsRead(ticket);
await SendWhatsAppMessage({
body: schedulingNotifies[0].message, ticket
});
DeleteSchedulingNotifyService(schedulingNotifies[0].id)
}
};
export const startSchedulingMonitor =async (mileseconds: number) => {
scheduler_monitor = setInterval(monitor, mileseconds)
}
export const stopSchedulingMonitor =async ( ) => {
clearInterval(scheduler_monitor)
}

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,52 @@
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
schedulingDate: Date;
@Column
message: 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

@ -11,12 +11,7 @@ const upload = multer(uploadConfig);
messageRoutes.get("/messages/:ticketId", isAuth, MessageController.index); messageRoutes.get("/messages/:ticketId", isAuth, MessageController.index);
messageRoutes.post( messageRoutes.post("/messages/:ticketId", isAuth, upload.array("medias"), MessageController.store);
"/messages/:ticketId",
isAuth,
upload.array("medias"),
MessageController.store
);
messageRoutes.delete("/messages/:messageId", isAuth, MessageController.remove); messageRoutes.delete("/messages/:messageId", isAuth, MessageController.remove);

View File

@ -4,6 +4,8 @@ import { initIO } from "./libs/socket";
import { logger } from "./utils/logger"; import { logger } from "./utils/logger";
import { StartAllWhatsAppsSessions } from "./services/WbotServices/StartAllWhatsAppsSessions"; import { StartAllWhatsAppsSessions } from "./services/WbotServices/StartAllWhatsAppsSessions";
import { startSchedulingMonitor } from "./helpers/SchedulingNotifySendMessage"
const server = app.listen(process.env.PORT, () => { const server = app.listen(process.env.PORT, () => {
logger.info(`Server started on port: ${process.env.PORT}`); logger.info(`Server started on port: ${process.env.PORT}`);
}); });
@ -11,3 +13,5 @@ const server = app.listen(process.env.PORT, () => {
initIO(server); initIO(server);
StartAllWhatsAppsSessions(); StartAllWhatsAppsSessions();
gracefulShutdown(server); gracefulShutdown(server);
startSchedulingMonitor(5000)

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,34 @@
import AppError from "../../errors/AppError";
import SchedulingNotify from "../../models/SchedulingNotify";
interface Request {
ticketId: string,
scheduleId: string,
schedulingDate: string,
message: string
}
const CreateSchedulingNotifyService = async (
{
ticketId,
scheduleId,
schedulingDate,
message
}: Request): Promise<SchedulingNotify> => {
const schedulingNotify = await SchedulingNotify.create(
{
ticketId,
scheduleId,
schedulingDate,
message
})
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,68 @@
import { Op, 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("date", Sequelize.col("schedulingDate")), `${searchParam.toLowerCase().trim()}`,
// ),
// };
let date = searchParam.split(' ')[0]
let hour = searchParam.split(' ')[1].split(':')[0]
let minute = searchParam.split(' ')[1].split(':')[1]
const whereCondition = {
[Op.and]: [
{
"$schedulingDate$": Sequelize.where(Sequelize.fn("date", Sequelize.col("schedulingDate")), `${date.trim()}`)
},
{
"$schedulingDate$": Sequelize.where(Sequelize.fn("hour", Sequelize.col("schedulingDate")), `${hour.trim()}`)
},
{
"$schedulingDate$": Sequelize.where(Sequelize.fn("minute", Sequelize.col("schedulingDate")), `${minute.trim()}`)
}
]
};
const limit = 1;
const offset = limit * (+pageNumber - 1);
const { count, rows: schedulingNotifies } = await SchedulingNotify.findAndCountAll({
raw: true,
where: whereCondition,
limit,
offset,
order: [["id", "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

@ -6,7 +6,7 @@
"@date-io/date-fns": "^1.3.13", "@date-io/date-fns": "^1.3.13",
"@emotion/react": "^11.7.1", "@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0", "@emotion/styled": "^11.6.0",
"@material-ui/core": "^4.11.0", "@material-ui/core": "^4.12.1",
"@material-ui/icons": "^4.9.1", "@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.56", "@material-ui/lab": "^4.0.0-alpha.56",
"@material-ui/pickers": "^3.3.10", "@material-ui/pickers": "^3.3.10",

View File

@ -0,0 +1,306 @@
import React, { useState, useEffect, useRef } from 'react';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
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 DatePicker from '../../Report/DatePicker'
import TimerPickerSelect from '../TimerPickerSelect'
import TextArea1 from '../TextArea'
import TextareaAutosize from '@mui/material/TextareaAutosize';
import { subHours, addHours } from "date-fns";
import { LocalConvenienceStoreOutlined } from '@material-ui/icons';
const Item = (props) => {
const { sx, ...other } = props;
return (
<Box
sx={{
bgcolor: (theme) => (theme.palette.mode === 'dark' ? '#101010' : '#fff'),
color: (theme) => (theme.palette.mode === 'dark' ? 'grey.300' : 'grey.800'),
border: '1px solid',
borderColor: (theme) =>
theme.palette.mode === 'dark' ? 'grey.800' : 'grey.300',
p: 1,
m: 1,
borderRadius: 2,
fontSize: '0.875rem',
fontWeight: '700',
...sx,
}}
{...other}
/>
);
}
Item.propTypes = {
sx: PropTypes.oneOfType([
PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool]),
),
PropTypes.func,
PropTypes.object,
]),
};
const Modal = (props) => {
const [open, setOpen] = useState(true);
const [scroll, /*setScroll*/] = useState('body');
const [scheduleId, setScheduling] = useState(null)
const [startDate, setDatePicker] = useState(new Date())
const [timerPicker, setTimerPicker] = useState(new Date())
const [textArea1, setTextArea1] = useState()
const [greetRemember, setGreet] = useState('')
const [data] = useState(props.schedules)
const [chatEnd, setChatEnd] = useState(false)
const handleCancel = (event, reason) => {
if (reason && reason === "backdropClick")
return;
setChatEnd(null)
setOpen(false);
};
function greetMessageSchedule(scheduleDate){
return `podemos confirmar sua consulta agendada para hoje às ${scheduleDate}?`
}
function formatedTimeHour(timer){
return `${timer.getHours().toString().padStart(2, '0')}:${timer.getMinutes().toString().padStart(2, '0')}`
}
const handleChatEnd = (event, reason) => {
if (reason && reason === "backdropClick")
return;
if (scheduleId === '1'){
}
else if(textArea1.trim().length<10){
alert('Mensagem muito curta!\nMínimo 10 caracteres.')
return
}
else if((new Date(timerPicker).getHours() > 16 && new Date(timerPicker).getMinutes() > 0) ||
(new Date(timerPicker).getHours() < 7)){
alert('Horário comercial inválido!\n Selecione um horário de lembrete válido entre às 07:00 e 17:00')
return
}
else if((new Date(startDate).toISOString().slice(0, 10) === new Date().toISOString().slice(0, 10))){
if(((new Date(timerPicker).getHours() == new Date().getHours()) &&
(new Date(timerPicker).getMinutes() <= new Date().getMinutes())) ||
(new Date(timerPicker).getHours() < new Date().getHours())
){
alert('Para agendamentos do dia, é necessário que o horário do lembrete seja maior que o horário atual!')
return
}
}
setChatEnd({
'scheduleId': scheduleId,
'schedulingDate': `${startDate} ${timerPicker.getHours()}:${timerPicker.getMinutes()}:${timerPicker.getSeconds()}`,
'message': textArea1
})
setOpen(false);
};
useEffect(()=>{
props.func(chatEnd);
}, [chatEnd])
const descriptionElementRef = useRef(null);
useEffect(() => {
if (open) {
const { current: descriptionElement } = descriptionElementRef;
if (descriptionElement !== null) {
descriptionElement.focus();
}
}
}, [open]);
// Get from child 1
const textFieldSelect = (data) => {
console.log('textFieldSelect: ',data);
setScheduling(data)
}
// Get from child 2
const datePickerValue = (data) => {
console.log('datePickerValue: ',(data));
setDatePicker(data)
}
// Get from child 3
const timerPickerValue = (data) => {
console.log('timerPickerValue: ',(data));
setTimerPicker(data)
}
// Get from child 4
// const textArea1Value = (data) => {
// console.log('textArea1Value: ',(data));
// setTextArea1(data)
// }
useEffect(()=>{
if (parseInt(timerPicker.getHours()) > 12 && parseInt(timerPicker.getHours()) < 18){
setTextArea1('Boa tarde, '+greetMessageSchedule( formatedTimeHour(addHours(new Date(timerPicker), 1))))
}
else if(parseInt(timerPicker.getHours()) < 12){
setTextArea1('Bom dia, '+greetMessageSchedule( formatedTimeHour(addHours(new Date(timerPicker), 1))))
}
else if(parseInt(timerPicker.getHours()) > 18){
setTextArea1('Boa noite, '+greetMessageSchedule( formatedTimeHour(addHours(new Date(timerPicker), 1))))
}
// console.log(
// 'addHours(new Date(timerPicker), 1): ', addHours(new Date(timerPicker), 1).getHours(),
// '\naddHours(new Date(timerPicker), 1): ', addHours(new Date(timerPicker), 1).getMinutes()
// )
},[timerPicker])
const handleChange = (event) => {
setTextArea1(event.target.value);
};
return (
<Dialog
open={open}
onClose={handleCancel}
// fullWidth={true}
// maxWidth={true}
disableEscapeKeyDown
scroll={scroll}
aria-labelledby="scroll-dialog-title"
aria-describedby="scroll-dialog-description"
>
<DialogTitle id="scroll-dialog-title">{props.modal_header}</DialogTitle>
<DialogContent dividers={scroll === 'body'}>
<DialogContentText
id="scroll-dialog-description"
ref={descriptionElementRef}
tabIndex={-1}
>
</DialogContentText>
<Box
sx={{
width: 500,
height: '100%',
// backgroundColor: 'primary.dark',
// '&:hover': {backgroundColor: 'primary.main', opacity: [0.9, 0.8, 0.7],},
}}>
<Box sx={{
display: 'grid',
}}>
<Item>
<span>Selecione uma opção para encerrar o Atendimento</span>
<Item>
<SelectField func={textFieldSelect} emptyField={false} header={'Opções de encerramento do atendimento'} currencies={data.map((obj)=>{
return {'value': obj.id, 'label': obj.name}
})}/>
</Item>
</Item>
</Box>
{scheduleId==='2' &&
<Item>
<span>Lembrete de retorno</span>
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)' }}>
<Item><DatePicker func={datePickerValue} title={'Data do retorno'}/></Item>
<Item><TimerPickerSelect func={timerPickerValue} title={'Hora do lembrete'}/></Item>
</Box>
<Box sx={{display: 'flex', flexDirection: 'column' }}>
{/* <Item><TextArea1 func={textArea1Value} greetRemember={greetRemember} hint={'Mensagem para cliente'}/></Item> */}
<Item>
<TextareaAutosize
aria-label="minimum height"
minRows={3}
value={textArea1}
placeholder={'Mensagem para lembrar cliente'}
onChange={ handleChange}
style={{ width: '100%' }}
/>
</Item>
</Box>
</Item>
}
</Box>
</DialogContent>
<DialogActions>
<div style={{marginRight:'50px'}}>
<Button onClick={handleCancel}>Cancelar</Button>
</div>
<Button onClick={handleChatEnd}>Ok</Button>
</DialogActions>
</Dialog>
);
}
export default Modal

View File

@ -0,0 +1,35 @@
import React, { useState, useEffect } from "react";
import TextareaAutosize from '@mui/material/TextareaAutosize';
const MinHeightTextarea = (props) => {
const [value, setValue] = useState('');
useEffect(()=>{
props.func(value);
}, [value])
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<TextareaAutosize
aria-label="minimum height"
minRows={3}
placeholder={props.hint}
defaultValue={props.greetRemember}
onChange={ handleChange}
style={{ width: '100%' }}
/>
);
}
export default MinHeightTextarea;

View File

@ -0,0 +1,86 @@
// 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 TextField from '@mui/material/TextField';
import DateFnsUtils from '@date-io/date-fns';
import {
TimePicker,
MuiPickersUtilsProvider,
} from '@material-ui/pickers';
import ptBrLocale from "date-fns/locale/pt-BR";
const ResponsiveTimePickers = (props) => {
const [value, setValue] = useState(new Date());
// props.func(value);
useEffect(()=>{
props.func(value);
}, [value])
return (
<Fragment>
<MuiPickersUtilsProvider utils={DateFnsUtils} locale={ptBrLocale}>
<TimePicker
variant="outline"
label={props.title}
value={value}
ampm={false}
onChange={(newValue) => {
setValue(newValue);
}}
// Ativar se necessario
// renderInput={(params) => <TextField {...params} />}
/>
</MuiPickersUtilsProvider>
</Fragment>
);
}
export default ResponsiveTimePickers

View File

@ -585,7 +585,8 @@ const MessageInput = ({ ticketStatus }) => {
: i18n.t("messagesInput.placeholderClosed") : i18n.t("messagesInput.placeholderClosed")
} }
multiline multiline
rowsMax={5} // rowsMax={5}
maxRows={5}
value={inputMessage} value={inputMessage}
onChange={handleChangeInput} onChange={handleChangeInput}
disabled={recording || loading || ticketStatus !== "open"} disabled={recording || loading || ticketStatus !== "open"}

View File

@ -190,7 +190,8 @@ const NotificationsPopOver = () => {
<> <>
<IconButton <IconButton
onClick={handleClick} onClick={handleClick}
buttonRef={anchorEl} // buttonRef={anchorEl}
ref={anchorEl}
aria-label="Open Notifications" aria-label="Open Notifications"
color="inherit" color="inherit"
> >

View File

@ -1,6 +1,6 @@
import React, { Fragment, useState } from "react"; import React, { Fragment, useState, useEffect } from "react";
import DateFnsUtils from '@date-io/date-fns'; // choose your lib import DateFnsUtils from '@date-io/date-fns'; // choose your lib
@ -24,8 +24,14 @@ function formatDateDatePicker(data){
function ResponsiveDatePickers(props) { function ResponsiveDatePickers(props) {
const [selectedDate, handleDateChange] = useState(new Date()); const [selectedDate, handleDateChange] = useState(new Date());
// props.func(formatDateDatePicker(selectedDate));
useEffect(()=>{
props.func(formatDateDatePicker(selectedDate)); props.func(formatDateDatePicker(selectedDate));
}, [selectedDate])
return ( return (
<Fragment> <Fragment>
@ -35,6 +41,7 @@ function ResponsiveDatePickers(props) {
variant="inline" variant="inline"
inputVariant="outlined" inputVariant="outlined"
label={props.title} label={props.title}
minDate={new Date()}
//format="MM/dd/yyyy" //format="MM/dd/yyyy"
format="dd/MM/yyyy" format="dd/MM/yyyy"
value={selectedDate} value={selectedDate}

View File

@ -1,16 +1,25 @@
import * as React from 'react'; import React, { useState, useEffect } from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import TextField from '@mui/material/TextField'; import TextField from '@mui/material/TextField';
const SelectTextFields = (props) => { const SelectTextFields = (props) => {
const [currency, setCurrency] = React.useState('0');
props.currencies.push({ value: '0', label: ''}) const [currency, setCurrency] = useState(props.emptyField ? '0' : '1');
if(props.emptyField){
props.currencies.push({ 'value': 0, 'label': ''})
}
useEffect(()=>{
props.func(currency); props.func(currency);
}, [currency])
const handleChange = (event) => { const handleChange = (event) => {
setCurrency(event.target.value); setCurrency(event.target.value);
}; };
@ -30,7 +39,8 @@ const SelectTextFields = (props) => {
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
'& .MuiTextField-root': { m: 1, width: '30ch' },}} '& .MuiTextField-root': { m: 1, },}
}
noValidate noValidate
autoComplete="off" autoComplete="off"
> >
@ -39,7 +49,7 @@ const SelectTextFields = (props) => {
margin="dense" margin="dense"
size="small" size="small"
select select
label="Usuário" label={props.header}
value={currency} value={currency}
onChange={handleChange} onChange={handleChange}
SelectProps={{ SelectProps={{
@ -47,13 +57,16 @@ const SelectTextFields = (props) => {
}} }}
// helperText="Please select your currency" // helperText="Please select your currency"
> >
{props.currencies.map((option) => (
<option key={option.value} value={option.value}> {props.currencies.map((option, index) => (
<option key={index} value={option.value}>
{option.label} {option.label}
</option> </option>
))} ))}
</TextField> </TextField>
</Box> </Box>

View File

@ -83,15 +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);
@ -161,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";
@ -12,6 +12,9 @@ import ButtonWithSpinner from "../ButtonWithSpinner";
import toastError from "../../errors/toastError"; import toastError from "../../errors/toastError";
import { AuthContext } from "../../context/Auth/AuthContext"; import { AuthContext } from "../../context/Auth/AuthContext";
import Modal from "../ChatEnd/ModalChatEnd";
import { render } from '@testing-library/react';
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
actionButtons: { actionButtons: {
marginRight: 6, marginRight: 6,
@ -24,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);
@ -32,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);
}; };
@ -40,16 +45,57 @@ const TicketActionButtons = ({ ticket }) => {
setAnchorEl(null); setAnchorEl(null);
}; };
const handleUpdateTicketStatus = async (e, status, userId) => {
const chatEndVal = (data) => {
if(data){
data = {...data, 'ticketId': ticket.id}
console.log('ChatEnd: ',(data));
handleUpdateTicketStatus(null, "closed", user?.id, data)
}
setChatEnd(data)
}
const handleModal = (/*status, userId*/) => {
render(<Modal
modal_header={'Finalização de Atendimento'}
func={chatEndVal}
schedules={schedule}
/>)
};
const handleUpdateTicketStatus = async (e, status, userId, schedulingData={}) => {
setLoading(true); setLoading(true);
try { try {
if(status==='closed'){
await api.put(`/tickets/${ticket.id}`, { await api.put(`/tickets/${ticket.id}`, {
status: status, status: status,
userId: userId || null, userId: userId || null,
schedulingNotifyData: JSON.stringify(schedulingData)
}); });
}
else{
await api.put(`/tickets/${ticket.id}`, {
status: status,
userId: userId || null
});
}
setLoading(false); setLoading(false);
if (status === "open") { if (status === "open") {
history.push(`/tickets/${ticket.id}`); history.push(`/tickets/${ticket.id}`);
@ -60,6 +106,10 @@ const TicketActionButtons = ({ ticket }) => {
setLoading(false); setLoading(false);
toastError(err); toastError(err);
} }
}; };
return ( return (
@ -90,7 +140,13 @@ 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()
// handleUpdateTicketStatus(e, "closed", user?.id)
}}
> >
{i18n.t("messagesList.header.buttons.resolve")} {i18n.t("messagesList.header.buttons.resolve")}
</ButtonWithSpinner> </ButtonWithSpinner>

View File

@ -262,7 +262,7 @@ const textFieldSelectUser = (data) => {
<MainContainer> <MainContainer>
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)' }}> <Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)' }}>
<Item><SelectField func={textFieldSelectUser} currencies={users.map((obj)=>{ <Item><SelectField func={textFieldSelectUser} emptyField={true} header={'Usuário'} currencies={users.map((obj)=>{
return {'value': obj.id, 'label': obj.name} return {'value': obj.id, 'label': obj.name}
})}/></Item> })}/></Item>