Skip to content

Commit

Permalink
Exportação e exclusão em massa de contatos
Browse files Browse the repository at this point in the history
  • Loading branch information
rtenorioh committed Aug 31, 2022
1 parent f1ad7bc commit 513f516
Show file tree
Hide file tree
Showing 8 changed files with 723 additions and 609 deletions.
14 changes: 12 additions & 2 deletions backend/src/controllers/ContactController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import CreateContactService from "../services/ContactServices/CreateContactServi
import ShowContactService from "../services/ContactServices/ShowContactService";
import UpdateContactService from "../services/ContactServices/UpdateContactService";
import DeleteContactService from "../services/ContactServices/DeleteContactService";
import DeleteAllContactService from "../services/ContactServices/DeleteAllContactService";

import CheckContactNumber from "../services/WbotServices/CheckNumber"
import CheckIsValidContact from "../services/WbotServices/CheckIsValidContact";
Expand Down Expand Up @@ -111,9 +112,7 @@ export const store = async (req: Request, res: Response): Promise<Response> => {

export const show = async (req: Request, res: Response): Promise<Response> => {
const { contactId } = req.params;

const contact = await ShowContactService(contactId);

return res.status(200).json(contact);
};

Expand Down Expand Up @@ -168,3 +167,14 @@ export const remove = async (

return res.status(200).json({ message: "Contact deleted" });
};

export const removeAll = async (
req: Request,
res: Response
): Promise<Response> => {
const { contactId } = req.params;

await DeleteAllContactService();

return res.send();
};
13 changes: 2 additions & 11 deletions backend/src/routes/contactRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,13 @@ import * as ImportPhoneContactsController from "../controllers/ImportPhoneContac

const contactRoutes = express.Router();

contactRoutes.post(
"/contacts/import",
isAuth,
ImportPhoneContactsController.store
);

contactRoutes.post("/contacts/import", isAuth, ImportPhoneContactsController.store);
contactRoutes.get("/contacts", isAuth, ContactController.index);

contactRoutes.get("/contacts/:contactId", isAuth, ContactController.show);

contactRoutes.post("/contacts", isAuth, ContactController.store);

contactRoutes.post("/contact", isAuth, ContactController.getContact);

contactRoutes.put("/contacts/:contactId", isAuth, ContactController.update);

contactRoutes.delete("/contacts/:contactId", isAuth, ContactController.remove);
contactRoutes.delete("/contacts", isAuth, ContactController.removeAll);

export default contactRoutes;
14 changes: 14 additions & 0 deletions backend/src/services/ContactServices/DeleteAllContactService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Contact from "../../models/Contact";
import AppError from "../../errors/AppError";

const DeleteAllContactService = async (): Promise<void> => {
await Contact.findAll();

if (!Contact) {
throw new AppError("ERR_NO_CONTACT_FOUND", 404);
}

await Contact.destroy({where: {} })
};

export default DeleteAllContactService;
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "frontend",
"version": "0.1.0",
"system": {
"version": "1.0.8",
"version": "1.1.0",
"name": "Press Ticket",
"url": "https://pressticket.com.br"
},
Expand All @@ -25,6 +25,7 @@
"qrcode.react": "^1.0.0",
"react": "^16.13.1",
"react-color": "^2.19.3",
"react-csv": "^2.2.2",
"react-dom": "^16.13.1",
"react-modal-image": "^2.5.0",
"react-router-dom": "^5.2.0",
Expand Down
182 changes: 131 additions & 51 deletions frontend/src/pages/Contacts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,50 @@ import React, { useState, useEffect, useReducer, useContext } from "react";
import openSocket from "../../services/socket-io";
import { toast } from "react-toastify";
import { useHistory } from "react-router-dom";
import { CSVLink } from "react-csv";

import { makeStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import Button from "@material-ui/core/Button";
import Avatar from "@material-ui/core/Avatar";
import WhatsAppIcon from "@material-ui/icons/WhatsApp";
import SearchIcon from "@material-ui/icons/Search";
import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment";

import IconButton from "@material-ui/core/IconButton";
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline";
import EditIcon from "@material-ui/icons/Edit";

import {
Avatar,
Button,
IconButton,
InputAdornment,
Paper,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
TextField,
Tooltip
} from "@material-ui/core";

import {
AddCircleOutline,
DeleteForever,
DeleteOutline,
ImportContacts,
Archive,
Edit,
Search,
WhatsApp
} from "@material-ui/icons";

import api from "../../services/api";
import { i18n } from "../../translate/i18n";

import TableRowSkeleton from "../../components/TableRowSkeleton";
import ContactModal from "../../components/ContactModal";
import ConfirmationModal from "../../components/ConfirmationModal/";

import { i18n } from "../../translate/i18n";
import MainHeader from "../../components/MainHeader";
import Title from "../../components/Title";
import MainHeaderButtonsWrapper from "../../components/MainHeaderButtonsWrapper";
import MainContainer from "../../components/MainContainer";
import { Can } from "../../components/Can";

import toastError from "../../errors/toastError";
import { AuthContext } from "../../context/Auth/AuthContext";
import { Can } from "../../components/Can";

const reducer = (state, action) => {
if (action.type === "LOAD_CONTACTS") {
Expand Down Expand Up @@ -86,6 +98,14 @@ const useStyles = makeStyles((theme) => ({
overflowY: "scroll",
...theme.scrollbarStyles,
},
csvbtn: {
textDecoration: 'none'
},
avatar: {
width: "50px",
height: "50px",
borderRadius:"25%"
}
}));

const Contacts = () => {
Expand All @@ -101,6 +121,7 @@ const Contacts = () => {
const [selectedContactId, setSelectedContactId] = useState(null);
const [contactModalOpen, setContactModalOpen] = useState(false);
const [deletingContact, setDeletingContact] = useState(null);
const [deletingAllContact, setDeletingAllContact] = useState(null);
const [confirmOpen, setConfirmOpen] = useState(false);
const [hasMore, setHasMore] = useState(false);

Expand Down Expand Up @@ -194,6 +215,19 @@ const Contacts = () => {
setPageNumber(1);
};

const handleDeleteAllContact = async () => {
try {
await api.delete("/contacts");
toast.success(i18n.t("contacts.toasts.deletedAll"));
history.go(0);
} catch (err) {
toastError(err);
}
setDeletingAllContact(null);
setSearchParam("");
setPageNumber();
};

const handleimportContact = async () => {
try {
await api.post("/contacts/import");
Expand Down Expand Up @@ -225,25 +259,26 @@ const Contacts = () => {
></ContactModal>
<ConfirmationModal
title={
deletingContact
? `${i18n.t("contacts.confirmationModal.deleteTitle")} ${deletingContact.name
}?`
: `${i18n.t("contacts.confirmationModal.importTitlte")}`
deletingContact ? `${i18n.t("contacts.confirmationModal.deleteTitle")} ${deletingContact.name}?`
: deletingAllContact ? `${i18n.t("contacts.confirmationModal.deleteAllTitle")}`
: `${i18n.t("contacts.confirmationModal.importTitle")}`
}
open={confirmOpen}
onClose={setConfirmOpen}
onConfirm={(e) =>
deletingContact
? handleDeleteContact(deletingContact.id)
: handleimportContact()
deletingContact ? handleDeleteContact(deletingContact.id)
: deletingAllContact ? handleDeleteAllContact(deletingAllContact)
: handleimportContact()
}
>
{deletingContact
? `${i18n.t("contacts.confirmationModal.deleteMessage")}`
: `${i18n.t("contacts.confirmationModal.importMessage")}`}
{
deletingContact ? `${i18n.t("contacts.confirmationModal.deleteMessage")}`
: deletingAllContact ? `${i18n.t("contacts.confirmationModal.deleteAllMessage")}`
: `${i18n.t("contacts.confirmationModal.importMessage")}`
}
</ConfirmationModal>
<MainHeader>
<Title>{i18n.t("contacts.title")}</Title>
<Title>{i18n.t("contacts.title")} ({contacts.length})</Title>
<MainHeaderButtonsWrapper>
<TextField
placeholder={i18n.t("contacts.searchPlaceholder")}
Expand All @@ -253,7 +288,7 @@ const Contacts = () => {
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon color="secondary" />
<Search color="secondary" />
</InputAdornment>
),
}}
Expand All @@ -263,23 +298,66 @@ const Contacts = () => {
perform="drawer-admin-items:view"
yes={() => (
<>
<Button
variant="contained"
color="primary"
onClick={(e) => setConfirmOpen(true)}
>
{i18n.t("contacts.buttons.import")}
</Button>
<Tooltip title={i18n.t("contacts.buttons.import")}>
<Button
variant="contained"
color="primary"
onClick={(e) => setConfirmOpen(true)}
>
<ImportContacts />
</Button>
</Tooltip>
</>
)}
/>
<Tooltip title={i18n.t("contacts.buttons.add")}>
<Button
variant="contained"
color="primary"
onClick={handleOpenContactModal}
>
<AddCircleOutline />
</Button>
</Tooltip>
<Tooltip title={i18n.t("contacts.buttons.export")}>
<CSVLink
className={classes.csvbtn}
separator=";"
filename={'pressticket-contacts.csv'}
data={
contacts.map((contact) => ({
name: contact.name,
number: contact.number,
email: contact.email
}))
}>
<Button
variant="contained"
color="primary">
<Archive />
</Button>
</CSVLink>
</Tooltip>
<Can
role={user.profile}
perform="drawer-admin-items:view"
yes={() => (
<>
<Tooltip title={i18n.t("contacts.buttons.delete")}>
<Button
variant="contained"
color="primary"
onClick={(e) => {
setConfirmOpen(true);
setDeletingAllContact(contacts);
}}
>
<DeleteForever />
</Button>
</Tooltip>
</>
)}
/>
<Button
variant="contained"
color="primary"
onClick={handleOpenContactModal}
>
{i18n.t("contacts.buttons.add")}
</Button>
</MainHeaderButtonsWrapper>
</MainHeader>
<Paper
Expand All @@ -291,7 +369,9 @@ const Contacts = () => {
<TableHead>
<TableRow>
<TableCell padding="checkbox" />
<TableCell>{i18n.t("contacts.table.name")}</TableCell>
<TableCell>
{i18n.t("contacts.table.name")}
</TableCell>
<TableCell align="center">
{i18n.t("contacts.table.whatsapp")}
</TableCell>
Expand All @@ -308,7 +388,7 @@ const Contacts = () => {
{contacts.map((contact) => (
<TableRow key={contact.id}>
<TableCell style={{ paddingRight: 0 }}>
{<Avatar src={contact.profilePicUrl} />}
{<Avatar src={contact.profilePicUrl} className={classes.avatar} />}
</TableCell>
<TableCell>{contact.name}</TableCell>
<TableCell align="center">{contact.number}</TableCell>
Expand All @@ -318,13 +398,13 @@ const Contacts = () => {
size="small"
onClick={() => handleSaveTicket(contact.id)}
>
<WhatsAppIcon color="secondary" />
<WhatsApp color="secondary" />
</IconButton>
<IconButton
size="small"
onClick={() => hadleEditContact(contact.id)}
>
<EditIcon color="secondary" />
<Edit color="secondary" />
</IconButton>
<Can
role={user.profile}
Expand All @@ -337,14 +417,14 @@ const Contacts = () => {
setDeletingContact(contact);
}}
>
<DeleteOutlineIcon color="secondary" />
<DeleteOutline color="secondary" />
</IconButton>
)}
/>
</TableCell>
</TableRow>
))}
{loading && <TableRowSkeleton avatar columns={3} />}
{loading && <TableRowSkeleton avatar columns={4} />}
</>
</TableBody>
</Table>
Expand Down
Loading

0 comments on commit 513f516

Please sign in to comment.