diff --git a/backend/src/controllers/ContactController copy.js b/backend/src/controllers/ContactController copy.js deleted file mode 100644 index 4e74310..0000000 --- a/backend/src/controllers/ContactController copy.js +++ /dev/null @@ -1,67 +0,0 @@ -const Contact = require("../models/Contact"); -const Message = require("../models/Message"); -const Sequelize = require("sequelize"); -const { getIO } = require("../libs/socket"); -const { getWbot } = require("../libs/wbot"); - -exports.index = async (req, res) => { - const { searchParam = "" } = req.query; - - const lowerSerachParam = searchParam.toLowerCase(); - - const whereCondition = { - name: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("name")), - "LIKE", - "%" + lowerSerachParam + "%" - ), - }; - - //todo >> add contact number to search where condition - - const contacts = await Contact.findAll({ - where: whereCondition, - attributes: { - include: [ - [ - Sequelize.literal(`( - SELECT COUNT(*) - FROM messages AS message - WHERE - message.contactId = contact.id - AND - message.read = 0 - - )`), - "unreadMessages", - ], - ], - }, - order: [["updatedAt", "DESC"]], - }); - - return res.json(contacts); -}; - -exports.store = async (req, res) => { - const wbot = getWbot(); - const io = getIO(); - const { number, name } = req.body; - - const result = await wbot.isRegisteredUser(`55${number}@c.us`); - - if (!result) { - return res - .status(400) - .json({ error: "The suplied number is not a valid Whatsapp number" }); - } - const profilePicUrl = await wbot.getProfilePicUrl(`55${number}@c.us`); - - const contact = await Contact.create({ - name, - number: `55${number}`, - profilePicUrl, - }); - - res.status(200).json(contact); -}; diff --git a/backend/src/controllers/ContactController.js b/backend/src/controllers/ContactController.js index 807d00a..e6bfd0d 100644 --- a/backend/src/controllers/ContactController.js +++ b/backend/src/controllers/ContactController.js @@ -1,29 +1,31 @@ const Sequelize = require("sequelize"); +const { Op } = require("sequelize"); const Contact = require("../models/Contact"); const ContactCustomField = require("../models/ContactCustomField"); -// const Message = require("../models/Message"); -// const Sequelize = require("sequelize"); const { getIO } = require("../libs/socket"); -// const { getWbot } = require("../libs/wbot"); +const { getWbot } = require("../libs/wbot"); exports.index = async (req, res) => { const { searchParam = "", pageNumber = 1, rowsPerPage = 10 } = req.query; const whereCondition = { - name: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("name")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ), + [Op.or]: [ + { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + "%" + searchParam.toLowerCase() + "%" + ), + }, + { number: { [Op.like]: `%${searchParam}%` } }, + ], }; let limit = +rowsPerPage; let offset = limit * (pageNumber - 1); - //todo >> add contact number to search where condition - const { count, rows: contacts } = await Contact.findAndCountAll({ where: whereCondition, limit, @@ -35,22 +37,27 @@ exports.index = async (req, res) => { }; exports.store = async (req, res) => { - // const wbot = getWbot(); + const wbot = getWbot(); const io = getIO(); const newContact = req.body; - // const result = await wbot.isRegisteredUser(`55${number}@c.us`); + const result = await wbot.isRegisteredUser(`${newContact.number}@c.us`); - // if (!result) { - // return res - // .status(400) - // .json({ error: "The suplied number is not a valid Whatsapp number" }); - // } - // const profilePicUrl = await wbot.getProfilePicUrl(`55${number}@c.us`); + if (!result) { + return res + .status(400) + .json({ error: "The suplied number is not a valid Whatsapp number" }); + } + const profilePicUrl = await wbot.getProfilePicUrl( + `${newContact.number}@c.us` + ); - const contact = await Contact.create(newContact, { - include: "extraInfo", - }); + const contact = await Contact.create( + { ...newContact, profilePicUrl }, + { + include: "extraInfo", + } + ); io.emit("contact", { action: "create", diff --git a/backend/src/controllers/ImportPhoneContactsController.js b/backend/src/controllers/ImportPhoneContactsController.js new file mode 100644 index 0000000..b410c08 --- /dev/null +++ b/backend/src/controllers/ImportPhoneContactsController.js @@ -0,0 +1,18 @@ +const Contact = require("../models/Contact"); +const { getIO } = require("../libs/socket"); +const { getWbot, init } = require("../libs/wbot"); + +exports.store = async (req, res, next) => { + const io = getIO(); + const wbot = getWbot(); + + const phoneContacts = await wbot.getContacts(); + + await Promise.all( + phoneContacts.map(async ({ number, name }) => { + await Contact.create({ number, name }); + }) + ); + + return res.status(200).json({ message: "contacts imported" }); +}; diff --git a/backend/src/models/Contact.js b/backend/src/models/Contact.js index ab900f9..43ab6e5 100644 --- a/backend/src/models/Contact.js +++ b/backend/src/models/Contact.js @@ -5,7 +5,7 @@ class Contact extends Sequelize.Model { super.init( { name: { type: Sequelize.STRING }, - number: { type: Sequelize.STRING }, + number: { type: Sequelize.STRING, allowNull: false, unique: true }, email: { type: Sequelize.STRING, allowNull: false, defaultValue: "" }, profilePicUrl: { type: Sequelize.STRING }, }, diff --git a/backend/src/routes/contacts.js b/backend/src/routes/contacts.js index 64aff36..6b22706 100644 --- a/backend/src/routes/contacts.js +++ b/backend/src/routes/contacts.js @@ -2,9 +2,12 @@ const express = require("express"); const isAuth = require("../middleware/is-auth"); const ContactController = require("../controllers/ContactController"); +const ImportPhoneContactsController = require("../controllers/ImportPhoneContactsController"); const routes = express.Router(); +routes.post("/contacts/import", isAuth, ImportPhoneContactsController.store); + routes.get("/contacts", isAuth, ContactController.index); routes.get("/contacts/:contactId", isAuth, ContactController.show); diff --git a/backend/src/services/wbotMessageListener.js b/backend/src/services/wbotMessageListener.js index 3f96da9..ba0b0b3 100644 --- a/backend/src/services/wbotMessageListener.js +++ b/backend/src/services/wbotMessageListener.js @@ -28,7 +28,7 @@ const verifyContact = async (msgContact, profilePicUrl) => { }; const verifyTicket = async contact => { - const [ticket, created] = await Ticket.findOrCreate({ + const [ticket] = await Ticket.findOrCreate({ where: { status: { [Op.or]: ["open", "pending"], diff --git a/frontend/src/components/ContactDrawer/index.js b/frontend/src/components/ContactDrawer/index.js index de38c71..488294b 100644 --- a/frontend/src/components/ContactDrawer/index.js +++ b/frontend/src/components/ContactDrawer/index.js @@ -151,7 +151,7 @@ const ContactDrawer = ({ open, handleDrawerClose, contact, loading }) => { setModalOpen(false)} aria-labelledby="form-dialog-title" contactId={contact.id} diff --git a/frontend/src/components/ContactModal/index.js b/frontend/src/components/ContactModal/index.js index 809650f..0ae351b 100644 --- a/frontend/src/components/ContactModal/index.js +++ b/frontend/src/components/ContactModal/index.js @@ -52,19 +52,13 @@ const useStyles = makeStyles(theme => ({ }, })); -const ContactModal = ({ modalOpen, onClose, contactId }) => { +const ContactModal = ({ open, onClose, contactId }) => { const classes = useStyles(); const initialState = { name: "", number: "", email: "", - extraInfo: [ - { - name: "", - value: "", - }, - ], }; const [contact, setContact] = useState(initialState); @@ -77,7 +71,7 @@ const ContactModal = ({ modalOpen, onClose, contactId }) => { }; fetchContact(); - }, [contactId, modalOpen]); + }, [contactId, open]); const handleClose = () => { onClose(); @@ -100,7 +94,7 @@ const ContactModal = ({ modalOpen, onClose, contactId }) => { return (
{ handleSubmit, isSubmitting, }) => ( - <> +
{contactId ? "Editar contato" : "Adicionar contato"} @@ -148,7 +142,7 @@ const ContactModal = ({ modalOpen, onClose, contactId }) => { name="number" value={values.number || ""} onChange={handleChange} - placeholder="Ex: 13912344321" + placeholder="Ex: 5513912344321" variant="outlined" margin="dense" required @@ -234,7 +228,7 @@ const ContactModal = ({ modalOpen, onClose, contactId }) => { Cancelar - +
)}
diff --git a/frontend/src/components/NewTicketModal/index.js b/frontend/src/components/NewTicketModal/index.js index c4dbf36..a1f21cd 100644 --- a/frontend/src/components/NewTicketModal/index.js +++ b/frontend/src/components/NewTicketModal/index.js @@ -9,7 +9,6 @@ import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import Autocomplete from "@material-ui/lab/Autocomplete"; import CircularProgress from "@material-ui/core/CircularProgress"; -import FormControl from "@material-ui/core/FormControl"; import { green } from "@material-ui/core/colors"; import { makeStyles } from "@material-ui/core/styles"; @@ -43,25 +42,33 @@ const NewTicketModal = ({ modalOpen, onClose, contactId }) => { const [options, setOptions] = useState([]); const [loading, setLoading] = useState(false); + const [searchParam, setSearchParam] = useState(""); const [selectedContact, setSelectedContact] = useState(null); useEffect(() => { + if (!modalOpen || searchParam.length < 3) return; setLoading(true); - const fetchContacts = async () => { - try { - const res = await api.get("contacts"); - setOptions(res.data.contacts); - } catch (err) { - alert(err); - } - }; + const delayDebounceFn = setTimeout(() => { + const fetchContacts = async () => { + try { + const res = await api.get("contacts", { + params: { searchParam, rowsPerPage: 20 }, + }); + setOptions(res.data.contacts); + setLoading(false); + } catch (err) { + alert(err); + } + }; - fetchContacts(); - setLoading(false); - }, []); + fetchContacts(); + }, 1000); + return () => clearTimeout(delayDebounceFn); + }, [searchParam, modalOpen]); const handleClose = () => { onClose(); + setSearchParam(""); setSelectedContact(null); }; @@ -82,6 +89,8 @@ const NewTicketModal = ({ modalOpen, onClose, contactId }) => { handleClose(); }; + console.log(options); + return (
{ option.name} + getOptionLabel={option => `${option.name} - ${option.number}`} onChange={(e, newValue) => { setSelectedContact(newValue); }} @@ -105,9 +114,11 @@ const NewTicketModal = ({ modalOpen, onClose, contactId }) => { renderInput={params => ( setSearchParam(e.target.value)} id="my-input" InputProps={{ ...params.InputProps, diff --git a/frontend/src/components/TicketsList/index.js b/frontend/src/components/TicketsList/index.js index 55ddebe..3464c18 100644 --- a/frontend/src/components/TicketsList/index.js +++ b/frontend/src/components/TicketsList/index.js @@ -211,8 +211,7 @@ const TicketsList = () => { const [loading, setLoading] = useState(); const [searchParam, setSearchParam] = useState(""); const [tab, setTab] = useState("open"); - - const [newTicketModalOpen, setNewTicketModalOpen] = useState(true); + const [newTicketModalOpen, setNewTicketModalOpen] = useState(false); useEffect(() => { if (!("Notification" in window)) { diff --git a/frontend/src/pages/Contacts/index.js b/frontend/src/pages/Contacts/index.js index e5689ae..2ef18ac 100644 --- a/frontend/src/pages/Contacts/index.js +++ b/frontend/src/pages/Contacts/index.js @@ -70,9 +70,6 @@ const useStyles = makeStyles(theme => ({ const Contacts = () => { const classes = useStyles(); - const token = localStorage.getItem("token"); - // const userId = localStorage.getItem("userId"); - const [loading, setLoading] = useState(true); const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(10); @@ -80,9 +77,7 @@ const Contacts = () => { const [searchParam, setSearchParam] = useState(""); const [contacts, setContacts] = useState([]); const [selectedContactId, setSelectedContactId] = useState(null); - - const [modalOpen, setModalOpen] = useState(false); - + const [contactModalOpen, setContactModalOpen] = useState(false); const [deletingContact, setDeletingContact] = useState(null); const [confirmOpen, setConfirmOpen] = useState(false); @@ -105,7 +100,7 @@ const Contacts = () => { fetchContacts(); }, 1000); return () => clearTimeout(delayDebounceFn); - }, [searchParam, page, token, rowsPerPage]); + }, [searchParam, page, rowsPerPage]); useEffect(() => { const socket = openSocket(process.env.REACT_APP_BACKEND_URL); @@ -163,19 +158,19 @@ const Contacts = () => { setSearchParam(event.target.value.toLowerCase()); }; - const handleClickOpen = () => { + const handleOpenContactModal = () => { setSelectedContactId(null); - setModalOpen(true); + setContactModalOpen(true); }; - const handleClose = () => { + const handleCloseContactModal = () => { setSelectedContactId(null); - setModalOpen(false); + setContactModalOpen(false); }; const hadleEditContact = contactId => { setSelectedContactId(contactId); - setModalOpen(true); + setContactModalOpen(true); }; const handleDeleteContact = async contactId => { @@ -185,24 +180,43 @@ const Contacts = () => { alert(err); } setDeletingContact(null); + setSearchParam(""); + setPage(0); + }; + + const handleimportContact = async () => { + try { + await api.post("/contacts/import"); + } catch (err) { + console.log(err); + } }; return ( handleDeleteContact(deletingContact.id)} + onConfirm={e => + deletingContact + ? handleDeleteContact(deletingContact.id) + : handleimportContact() + } > - Tem certeza que deseja deletar este contato? Todos os tickets - relacionados serão perdidos. + {deletingContact + ? "Tem certeza que deseja deletar este contato? Todos os tickets relacionados serão perdidos." + : "Deseja importas todos os contatos do telefone? Essa função é experimental, você terá que recarregar a página após a importação "}
@@ -223,10 +237,18 @@ const Contacts = () => { ), }} /> - -
@@ -237,7 +259,7 @@ const Contacts = () => { Nome - Telefone + Whatsapp Email Ações