CRM App with React
Published: 2021-12-27 08:14:22

---
title: CRM App with React
published: true
description: 
tags: 
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k3wejk702ykj6wqm3ma1.png
 
---

CRM or the Customer Relationship Management system is a great way to administrate the relationship with the customer. It’s a great tool to know and a pure pleasure to build.

In this article, we will discuss how to create a CRM app using `React.js`, `Material UI`, we will be also using other integrations such as **Asana** to create a ticket whenever a ticket is create on our app update a **Google Sheet** whenever a deal is added to the user.

## Let's get started

- Setting up React

Let's begin by creating a boilerplate using the 
`create-react-app`
    
```jsx
npx create-react-app crm-app
```
    
- Install Material UI
    
We will be using Material UI as an UI library. It boosts up the speed of development as we won't have to much of CSS manually.
    
```jsx
npm install @mui/material @emotion/react @emotion/styled @material-ui/icons @mui/icons-material
```
    
- Create a  ContentTable component
    
We are using a Table component by Material UI to quickly set up a table with pagination support, we will be using **`Custom pagination actions`** you can read more about it [here](https://mui.com/components/tables/#main-content). 
    
This will give us a separate component. That handles pagination, for the sake of simplicity we will be extracting that function into another component.
    
To create your first component, create a directory inside src directory and call it components. Proceed by creating another directory inside ‘components’ and we will name it ContentTable, we will create a file with the same name inside of it, ContentTable.js.
    
```jsx
    const customers = [
    {
    id: 1,
    name: "Lillian Carter",
    email: "xcollier@goodwin.com",
    phone: "+1-267-551-8666",
    company: "Larkin Group",
    label: "Marketing",
    },
    {
    id: 2,
    name: "Otto Walker",
    email: "stokes.hubert@hotmail.com",
    phone: "+1-580-977-4361",
    company: "Bednar-Sawayn",
    label: "Newsletter",
    },
    {
    id: 3,
    name: "Kaylee Taylor",
    email: "diana45@hotmail.com",
    phone: "+1-202-918-2132",
    company: "Rolfson and Sons ",
    label: "Ads",
    },
    {
    id: 4,
    name: "Aiden Houston",
    email: "ctromp@kassulke.info",
    phone: "+1-215-480-3687",
    company: "Wisoky, Windler and Nienow",
    label: "Newsletter",
    },
    {
    id: 5,
    name: "Davis Houston",
    email: "voreilly@yahoo.com",
    phone: "+1-203-883-5460",
    company: "Schmidt, Streich and Schuster",
    label: "Ads",
    },
];
```
    
src/components/ListCustomer/ListCustomer.js
    
And, will pretty much copy the code for Table from Material UI’s documentation, and we will loop through our static array to fill the data in the table.
    
```jsx
import { React, useState } from "react";

import {
  Table,
  TableBody,
  TableContainer,
  TableFooter,
  TablePagination,
  TableRow,
  Paper,
  TableCell,
  TableHead,
} from "@mui/material";

const customers = [
  {
    id: 1,
    name: "Lillian Carter",
    email: "xcollier@goodwin.com",
    phone: "+1-267-551-8666",
    company: "Larkin Group",
    label: "Marketing",
  },
  {
    id: 2,
    name: "Otto Walker",
    email: "stokes.hubert@hotmail.com",
    phone: "+1-580-977-4361",
    company: "Bednar-Sawayn",
    label: "Newsletter",
  },
  {
    id: 3,
    name: "Kaylee Taylor",
    email: "diana45@hotmail.com",
    phone: "+1-202-918-2132",
    company: "Rolfson and Sons ",
    label: "Ads",
  },
  {
    id: 4,
    name: "Aiden Houston",
    email: "ctromp@kassulke.info",
    phone: "+1-215-480-3687",
    company: "Wisoky, Windler and Nienow",
    label: "Newsletter",
  },
  {
    id: 5,
    name: "Davis Houston",
    email: "voreilly@yahoo.com",
    phone: "+1-203-883-5460",
    company: "Schmidt, Streich and Schuster",
    label: "Ads",
  },
];

const ContentTable = () => {
  const [page, setPage] = useState(0);
  const [rowsPerPage, setRowsPerPage] = useState(5);
  const emptyRows =
    page > 0 ? Math.max(0, (1 + page) * rowsPerPage - customers.length) : 0;

  const handleChangePage = (event, newPage) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (event) => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  return (
    <TableContainer component={Paper} sx={{ margin: "2rem", width: "95%" }}>
      <Table sx={{ minWidth: 500 }} aria-label="custom pagination table">
        <TableHead>
          <TableRow>
            <TableCell
              align="left"
              sx={{
                backgroundColor: "black",
                color: "white",
                borderRight: "1px solid white",
              }}
            >
              Name
            </TableCell>
            <TableCell
              align="left"
              sx={{
                backgroundColor: "black",
                color: "white",
                borderRight: "1px solid white",
              }}
            >
              Company
            </TableCell>
            <TableCell
              align="left"
              sx={{
                backgroundColor: "black",
                color: "white",
                borderRight: "1px solid white",
              }}
            >
              Email
            </TableCell>
            <TableCell
              align="left"
              sx={{
                backgroundColor: "black",
                color: "white",
                borderRight: "1px solid white",
              }}
            >
              Phone
            </TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {customers &&
            (rowsPerPage > 0
              ? customers.slice(
                  page * rowsPerPage,
                  page * rowsPerPage + rowsPerPage
                )
              : customers
            ).map((row, index) => (
              <TableRow key={index}>
                <TableCell
                  component="th"
                  scope="row"
                  sx={{ width: 160, borderRight: "1px solid black" }}
                >
                  {row.name}
                </TableCell>
                <TableCell
                  sx={{ width: 160, borderRight: "1px solid black" }}
                  align="left"
                >
                  {row.company}
                </TableCell>
                <TableCell
                  sx={{ width: 160, borderRight: "1px solid black" }}
                  align="left"
                >
                  {row.email}
                </TableCell>
                <TableCell sx={{ width: 160 }} align="left">
                  {row.phone}
                </TableCell>
              </TableRow>
            ))}

          {emptyRows > 0 && (
            <TableRow style={{ height: 53 * emptyRows }}>
              <TableCell colSpan={6} />
            </TableRow>
          )}
        </TableBody>
        <TableFooter>
          <TableRow>
            <TablePagination
              rowsPerPageOptions={[5, 10, 25, { label: "All", value: -1 }]}
              colSpan={3}
              count={customers}
              rowsPerPage={rowsPerPage}
              page={page}
              SelectProps={{
                inputProps: {
                  "aria-label": "rows per page",
                },
                native: true,
              }}
              onPageChange={handleChangePage}
              onRowsPerPageChange={handleChangeRowsPerPage}
              ActionsComponent={TablePaginationActions}
            />
          </TableRow>
        </TableFooter>
      </Table>
    </TableContainer>
  );
};

export default ContentTable;

```
    
src/components/ContentTable/ContentTable.js
    
The above code will complain about `TablePaginationActions` being undefined, so let’s handle that.
    
Next, we will create a utility component, that will help us with Pagination, Material UI uses it on same component as a demo, but extracting it outside makes ContentTable bit more clean.
    
Create a directory in `componenets` and name it Pagination and create a index.js file inside of it.
    
```jsx
import { React } from "react";
import PropTypes from "prop-types";
import { useTheme } from "@mui/material/styles";
import { Box, IconButton } from "@mui/material";
import FirstPageIcon from "@mui/icons-material/FirstPage";
import KeyboardArrowLeft from "@mui/icons-material/KeyboardArrowLeft";
import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight";
import LastPageIcon from "@mui/icons-material/LastPage";

export const TablePaginationActions = (props) => {
  const theme = useTheme();
  const { count, page, rowsPerPage, onPageChange } = props;

  const handleFirstPageButtonClick = (event) => {
    onPageChange(event, 0);
  };

  const handleBackButtonClick = (event) => {
    onPageChange(event, page - 1);
  };

  const handleNextButtonClick = (event) => {
    onPageChange(event, page + 1);
  };

  const handleLastPageButtonClick = (event) => {
    onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1));
  };

  return (
    <Box sx={{ flexShrink: 0, ml: 2.5 }}>
      <IconButton
        onClick={handleFirstPageButtonClick}
        disabled={page === 0}
        aria-label="first page"
      >
        {theme.direction === "rtl" ? <LastPageIcon /> : <FirstPageIcon />}
      </IconButton>
      <IconButton
        onClick={handleBackButtonClick}
        disabled={page === 0}
        aria-label="previous page"
      >
        {theme.direction === "rtl" ? (
          <KeyboardArrowRight />
        ) : (
          <KeyboardArrowLeft />
        )}
      </IconButton>
      <IconButton
        onClick={handleNextButtonClick}
        disabled={page >= Math.ceil(count / rowsPerPage) - 1}
        aria-label="next page"
      >
        {theme.direction === "rtl" ? (
          <KeyboardArrowLeft />
        ) : (
          <KeyboardArrowRight />
        )}
      </IconButton>
      <IconButton
        onClick={handleLastPageButtonClick}
        disabled={page >= Math.ceil(count / rowsPerPage) - 1}
        aria-label="last page"
      >
        {theme.direction === "rtl" ? <FirstPageIcon /> : <LastPageIcon />}
      </IconButton>
    </Box>
  );
};

TablePaginationActions.propTypes = {
  count: PropTypes.number.isRequired,
  onPageChange: PropTypes.func.isRequired,
  page: PropTypes.number.isRequired,
  rowsPerPage: PropTypes.number.isRequired,
};

```
    
src/components/Pagination/index.js
    
We can now import this Pagination Component in the ContentTable component and the error will go away. The end result should look like this
    
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o58nbqm89c1di48cq12m.png)
 
    
- Create a Modal component
    
Now our table is ready, let’s work Modal component, we want this modal to pop up anytime a user clicks on any of the row on the table. We will be using Material UI’s Modal component for this you can read more about it [here](https://mui.com/components/modal/#main-content)
    
In a modal, we want 3 columns:
    
- 1st column would display more information about the particular customer
- 2nd column would accommodate the notes feature, where a user would be able to leave notes on the customer.
- 3rd column would a feature to add a deal to user and create a task on Asana.
    
It would look something like this
    
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wb5qtjzdj9soi1l35rw.png)
 
Let’s get to it.
    
```jsx
import { React } from "react";
import { styled } from "@mui/material/styles";
import { Typography, Fade, Modal, Grid, Paper, Backdrop } from "@mui/material";

const Item = styled(Paper)(({ theme }) => ({
  ...theme.typography.body2,
  padding: theme.spacing(1),
  textAlign: "center",
  color: theme.palette.text.secondary,
  boxShadow: "none",
}));

export default function Modals() {
  return (
    <div>
      <Modal
        aria-labelledby="transition-modal-title"
        aria-describedby="transition-modal-description"
        open={open}
        closeAfterTransition
        BackdropComponent={Backdrop}
        BackdropProps={{
          timeout: 500,
        }}
      >
        <Fade in={open}>
          <Grid
            container
            spacing={1}
            sx={{
              width: "95%",
              height: "95%",
              backgroundColor: "white",
              position: "absolute",
              top: "4%",
              left: "3%",
            }}
          >
            <Grid item xs={3} sx={{ padding: "5px" }}>
              <Typography variant="h6" gutterBottom component="div">
                Name
              </Typography>
              <Typography variant="h6" gutterBottom component="div">
                Company
              </Typography>
              <Typography variant="h6" gutterBottom component="div">
                Phone
              </Typography>
              <Typography variant="h6" gutterBottom component="div">
                Email
              </Typography>
              <Typography variant="h6" gutterBottom component="div">
                Label
              </Typography>
            </Grid>
            <Grid
              item
              xs={6}
              sx={{ backgroundColor: "lightgray", padding: "5px" }}
            >
              <Item sx={{ backgroundColor: "inherit", marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Notes
                </Typography>
              </Item>
            </Grid>
            <Grid item xs={3} sx={{ padding: "5px", marginTop: "2rem" }}>
              <Item>
                <Typography variant="h6" gutterBottom component="div">
                  Deals
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the revenue opportunities associated with this record
                </Typography>
              </Item>
              <Item sx={{ marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Tickets
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the customer requests associated with this record
                </Typography>
              </Item>
            </Grid>
          </Grid>
        </Fade>
      </Modal>
    </div>
  );
}
```
src/components/Modal/Modal.js
    
We are heavily utilizing the Material UI’s Grid layout to make this column structure you can get more read on it [here](https://mui.com/components/grid/#main-content)
    
The end result would look like this
    
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z2gf62l3p5uqkz2qakqx.png)
 
But you have no way to see this, since nothing is triggering this component to open up. Let get that done.
    
We need to do 4 things in ContentTable component now:
    
- Import the Modal component.
- Create a state to hold the status of whether Modal is open or not.
- Create a onClick trigger on the table’s row.
- Feed the customer’s data to Modal component.
    
```jsx
import Modals from "../Modal";
    
const [isOpen, setIsOpen] = useState();
const [customerData, setCustomerData] = useState();
    
const handleClick = (data) => {
    setIsOpen(true);
    setCustomerData(data);
    };
    
<TableRow
key={index}
onClick={() => {
    handleClick(row); //We are passing customer's information though this 'row' parameter
}}
sx={{ cursor: "pointer" }}
>
    
<Modals data={customerData} open={isOpen} setIsOpen={setIsOpen} />
```
    
Now our task of setting up the modal is done. This is how the ContentTable component should look like:
    
```jsx
import { React, useState } from "react";

import {
  Table,
  TableBody,
  TableContainer,
  TableFooter,
  TablePagination,
  TableRow,
  Paper,
  TableCell,
  TableHead,
} from "@mui/material";

import { TablePaginationActions } from "../Pagination";
import Modals from "../Modal";

const customers = [
  {
    id: 1,
    name: "Lillian Carter",
    email: "xcollier@goodwin.com",
    phone: "+1-267-551-8666",
    company: "Larkin Group",
    label: "Marketing",
  },
  {
    id: 2,
    name: "Otto Walker",
    email: "stokes.hubert@hotmail.com",
    phone: "+1-580-977-4361",
    company: "Bednar-Sawayn",
    label: "Newsletter",
  },
  {
    id: 3,
    name: "Kaylee Taylor",
    email: "diana45@hotmail.com",
    phone: "+1-202-918-2132",
    company: "Rolfson and Sons ",
    label: "Ads",
  },
  {
    id: 4,
    name: "Aiden Houston",
    email: "ctromp@kassulke.info",
    phone: "+1-215-480-3687",
    company: "Wisoky, Windler and Nienow",
    label: "Newsletter",
  },
  {
    id: 5,
    name: "Davis Houston",
    email: "voreilly@yahoo.com",
    phone: "+1-203-883-5460",
    company: "Schmidt, Streich and Schuster",
    label: "Ads",
  },
];

const ContentTable = () => {
  const [page, setPage] = useState(0);
  const [rowsPerPage, setRowsPerPage] = useState(5);
  const [isOpen, setIsOpen] = useState();
  const [customerData, setCustomerData] = useState();

  const emptyRows =
    page > 0 ? Math.max(0, (1 + page) * rowsPerPage - customers.length) : 0;

  const handleChangePage = (event, newPage) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (event) => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };
  const handleClick = (data) => {
    setIsOpen(true);
    setCustomerData(data);
  };

  return (
    <TableContainer component={Paper} sx={{ margin: "2rem", width: "95%" }}>
      <Table sx={{ minWidth: 500 }} aria-label="custom pagination table">
        <TableHead>
          <TableRow>
            <TableCell
              align="left"
              sx={{
                backgroundColor: "black",
                color: "white",
                borderRight: "1px solid white",
              }}
            >
              Name
            </TableCell>
            <TableCell
              align="left"
              sx={{
                backgroundColor: "black",
                color: "white",
                borderRight: "1px solid white",
              }}
            >
              Company
            </TableCell>
            <TableCell
              align="left"
              sx={{
                backgroundColor: "black",
                color: "white",
                borderRight: "1px solid white",
              }}
            >
              Email
            </TableCell>
            <TableCell
              align="left"
              sx={{
                backgroundColor: "black",
                color: "white",
                borderRight: "1px solid white",
              }}
            >
              Phone
            </TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {customers &&
            (rowsPerPage > 0
              ? customers.slice(
                  page * rowsPerPage,
                  page * rowsPerPage + rowsPerPage
                )
              : customers
            ).map((row, index) => (
              <TableRow
                key={index}
                onClick={() => {
                  handleClick(row);
                }}
                sx={{ cursor: "pointer" }}
              >
                <TableCell
                  component="th"
                  scope="row"
                  sx={{ width: 160, borderRight: "1px solid black" }}
                >
                  {row.name}
                </TableCell>
                <TableCell
                  sx={{ width: 160, borderRight: "1px solid black" }}
                  align="left"
                >
                  {row.company}
                </TableCell>
                <TableCell
                  sx={{ width: 160, borderRight: "1px solid black" }}
                  align="left"
                >
                  {row.email}
                </TableCell>
                <TableCell sx={{ width: 160 }} align="left">
                  {row.phone}
                </TableCell>
              </TableRow>
            ))}

          {emptyRows > 0 && (
            <TableRow style={{ height: 53 * emptyRows }}>
              <TableCell colSpan={6} />
            </TableRow>
          )}
        </TableBody>
        <TableFooter>
          <TableRow>
            <TablePagination
              rowsPerPageOptions={[5, 10, 25, { label: "All", value: -1 }]}
              colSpan={3}
              count={customers}
              rowsPerPage={rowsPerPage}
              page={page}
              SelectProps={{
                inputProps: {
                  "aria-label": "rows per page",
                },
                native: true,
              }}
              onPageChange={handleChangePage}
              onRowsPerPageChange={handleChangeRowsPerPage}
              ActionsComponent={TablePaginationActions}
            />
          </TableRow>
        </TableFooter>
      </Table>
      <Modals data={customerData} open={isOpen} setIsOpen={setIsOpen} />
    </TableContainer>
  );
};

export default ContentTable;
```    
src/components/ContentTable/ContentTable.js
    
Now if you click on any row on the table, the Modal we created should pop-up.
    
Now we are receiving 3 props in Modal component, 
    
- data - that contains the data of that row’s customer
- open - is a state containing a boolean value of whether modal is open or not
- setIsOpen - which is used to manipulate the value of ‘open’
    
Let’s utilize this.
    
```jsx
export default function Modals({ data, open, setIsOpen }) 
    
const handleClose = () => {
    setIsOpen(!open);
    };

<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
    timeout: 500,
}}
>

<Grid item xs={3} sx={{ padding: "5px" }}>
<Typography variant="h6" gutterBottom component="div">
    {data.name}
</Typography>
<Typography variant="h6" gutterBottom component="div">
    {data.company}
</Typography>
<Typography variant="h6" gutterBottom component="div">
    {data.phone}
</Typography>
<Typography variant="h6" gutterBottom component="div">
    {data.email}
</Typography>
<Typography variant="h6" gutterBottom component="div">
    {data.label}
</Typography>
</Grid>
```
    
Now our Modal component should look like this:
    
```jsx
import { React } from "react";
import { styled } from "@mui/material/styles";
import { Typography, Fade, Modal, Grid, Paper, Backdrop } from "@mui/material";

const Item = styled(Paper)(({ theme }) => ({
  ...theme.typography.body2,
  padding: theme.spacing(1),
  textAlign: "center",
  color: theme.palette.text.secondary,
  boxShadow: "none",
}));

export default function Modals({ data, open, setIsOpen }) {
  const handleClose = () => {
    setIsOpen(!open);
  };

  return (
    <div>
      <Modal
        aria-labelledby="transition-modal-title"
        aria-describedby="transition-modal-description"
        open={open}
        onClose={handleClose}
        closeAfterTransition
        BackdropComponent={Backdrop}
        BackdropProps={{
          timeout: 500,
        }}
      >
        <Fade in={open}>
          <Grid
            container
            spacing={1}
            sx={{
              width: "95%",
              height: "95%",
              backgroundColor: "white",
              position: "absolute",
              top: "4%",
              left: "3%",
            }}
          >
            <Grid item xs={3} sx={{ padding: "5px" }}>
              <Typography variant="h6" gutterBottom component="div">
                **{data.name}**
              </Typography>
              <Typography variant="h6" gutterBottom component="div">
                **{data.company}**
              </Typography>
              <Typography variant="h6" gutterBottom component="div">
                **{data.phone}**
              </Typography>
              <Typography variant="h6" gutterBottom component="div">
                **{data.email}**
              </Typography>
              <Typography variant="h6" gutterBottom component="div">
                **{data.label}**
              </Typography>
            </Grid>
            <Grid
              item
              xs={6}
              sx={{ backgroundColor: "lightgray", padding: "5px" }}
            >
              <Item sx={{ backgroundColor: "inherit", marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Notes
                </Typography>
              </Item>
            </Grid>
            <Grid item xs={3} sx={{ padding: "5px", marginTop: "2rem" }}>
              <Item>
                <Typography variant="h6" gutterBottom component="div">
                  Deals
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the revenue opportunities associated with this record
                </Typography>
              </Item>
              <Item sx={{ marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Tickets
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the customer requests associated with this record
                </Typography>
              </Item>
            </Grid>
          </Grid>
        </Fade>
      </Modal>
    </div>
  );
}
```    
src/components/Modal/Modal.js
    
The modal should have the dynamic data based on which row you click, and should look something like this:
    
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gll9gbba3afbmprcfy9e.png)
 
- Create a DetailsCard component.
    
Our bare-bones modal is ready, but it doesn’t look good at all, let’s extract out user’s information to a separate component, so we can enhance the looks.
    
Proceed by creating DetailsCard directory inside components, and create a DetailsCards.js inside it. We will be using Material UI’s Card component, you can read more about it [here](https://mui.com/components/cards/#main-content)
    
```jsx
import React from "react";
    
import { Fab, Typography, CardContent, Card } from "@mui/material";

const DetailsCard = () => {
    return (
    <>
        <Card sx={{ minWidth: 250, marginBottom: "2rem", marginTop: "2rem" }}>
        <CardContent>
            <Typography
            variant="body"
            gutterBottom
            component="div"
            sx={{ textAlign: "center", marginBottom: "1rem" }}
            >
            Personal Details
            </Typography>
            <Typography variant="h5" gutterBottom component="div">
            Name
            </Typography>
            <Typography variant="h6" gutterBottom component="div">
            Last Name
            </Typography>
        </CardContent>
        </Card>

        <Card sx={{ minWidth: 250, marginBottom: "2rem" }}>
        <CardContent>
            <Typography
            variant="body"
            gutterBottom
            component="div"
            sx={{ textAlign: "center", marginBottom: "1rem" }}
            >
            Professional Details
            </Typography>
            <Typography variant="h6" gutterBottom component="div">
            Company
            </Typography>
            <Typography variant="h6" gutterBottom component="div">
            Email
            </Typography>
        </CardContent>
        </Card>

        <Card sx={{ minWidth: 250 }}>
        <CardContent>
            <Typography
            variant="body"
            gutterBottom
            component="div"
            sx={{ textAlign: "center", marginBottom: "1rem" }}
            >
            Labels
            </Typography>
            <Fab variant="extended" size="small">
            Labels
            </Fab>
        </CardContent>
        </Card>
    </>
    );
};
export default DetailsCard;
```
src/components/DetailsCard/DetailsCard.js
    
All we have to do now is import and feed the customer data as props in Modal component
    
The Modal component would look like this after importing and feeding in the customer data 
    
```jsx
import { React } from "react";
import { styled } from "@mui/material/styles";
import { Typography, Fade, Modal, Grid, Paper, Backdrop } from "@mui/material";

import DetailsCard from "../DetailsCard";

const Item = styled(Paper)(({ theme }) => ({
  ...theme.typography.body2,
  padding: theme.spacing(1),
  textAlign: "center",
  color: theme.palette.text.secondary,
  boxShadow: "none",
}));

export default function Modals({ data, open, setIsOpen }) {
  const handleClose = () => {
    setIsOpen(!open);
  };

  return (
    <div>
      <Modal
        aria-labelledby="transition-modal-title"
        aria-describedby="transition-modal-description"
        open={open}
        onClose={handleClose}
        closeAfterTransition
        BackdropComponent={Backdrop}
        BackdropProps={{
          timeout: 500,
        }}
      >
        <Fade in={open}>
          <Grid
            container
            spacing={1}
            sx={{
              width: "95%",
              height: "95%",
              backgroundColor: "white",
              position: "absolute",
              top: "4%",
              left: "3%",
            }}
          >
            <Grid item xs={3} sx={{ padding: "5px" }}>
              <DetailsCard
                name={data.name}
                company={data.company}
                email={data.email}
                phone={data.phone}
                labels={data.labels}
              />
            </Grid>
            <Grid
              item
              xs={6}
              sx={{ backgroundColor: "lightgray", padding: "5px" }}
            >
              <Item sx={{ backgroundColor: "inherit", marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Notes
                </Typography>
              </Item>
            </Grid>
            <Grid item xs={3} sx={{ padding: "5px", marginTop: "2rem" }}>
              <Item>
                <Typography variant="h6" gutterBottom component="div">
                  Deals
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the revenue opportunities associated with this record
                </Typography>
              </Item>
              <Item sx={{ marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Tickets
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the customer requests associated with this record
                </Typography>
              </Item>
            </Grid>
          </Grid>
        </Fade>
      </Modal>
    </div>
  );
}

```
src/components/Modal/Modal.js
    
And, after utilizing the received props, the DetailsCard component should look like this
    
```jsx
import React from "react";
    
import { Fab, Typography, CardContent, Card } from "@mui/material";

const DetailsCard = ({ name, company, email, phone, labels }) => {
    return (
    <>
        <Card sx={{ minWidth: 250, marginBottom: "2rem", marginTop: "2rem" }}>
        <CardContent>
            <Typography
            variant="body"
            gutterBottom
            component="div"
            sx={{ textAlign: "center", marginBottom: "1rem" }}
            >
            Personal Details
            </Typography>
            <Typography variant="h5" gutterBottom component="div">
            {name}
            </Typography>
            <Typography variant="h6" gutterBottom component="div">
            {phone}
            </Typography>
        </CardContent>
        </Card>

        <Card sx={{ minWidth: 250, marginBottom: "2rem" }}>
        <CardContent>
            <Typography
            variant="body"
            gutterBottom
            component="div"
            sx={{ textAlign: "center", marginBottom: "1rem" }}
            >
            Professional Details
            </Typography>
            <Typography variant="h6" gutterBottom component="div">
            {company}
            </Typography>
            <Typography variant="h6" gutterBottom component="div">
            {email}
            </Typography>
        </CardContent>
        </Card>

        <Card sx={{ minWidth: 250 }}>
        <CardContent>
            <Typography
            variant="body"
            gutterBottom
            component="div"
            sx={{ textAlign: "center", marginBottom: "1rem" }}
            >
            Labels
            </Typography>
            <Fab variant="extended" size="small">
            {labels}
            </Fab>
        </CardContent>
        </Card>
    </>
    );
};
export default DetailsCard;
```
src/components/DetailsCard/DetailsCard.js
    
The end result will look like this
    
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s6bswkenaalrc54doi1r.png)

Much better now!
    
Before we can proceed with Notes, we need to get our backend ready. As we will be storing the notes in database for data persistence. Thankfully, creating a backend on Canonic is a breeze 
    
**Time to get your APIs ready!**
    
Getting your APIs for CRM app comes at real ease. All you have to do is clone this production-ready [project](https://app.canonic.dev/projects/61b86184b6586d0026b0b88e) on Canonic and you're done. It will provide you with the backend, APIs, and documentation you need for integration, without writing any code. You will need to create entries for customers on CMS, you can simply copy the contents from `customer` statics array we created.
    

## Backend integration with GraphQL

Let's now integrate! Now that we have our APIs ready, let's move on by installing **GraphQL packages.** 

- **Install GraphQL packages**
    
To pull our data from the backend, we will need two packages - Apollo Client and GraphQL 
    
```jsx
npm i @apollo/client graphql
```
    
- **Configure GraphQL to communicate with backend** 
    
Configure the Apollo Client in the project directory, inside `index.js` configure your apollo client so it would communicate with the backend. 
    
> Note to replace the `uri` with the one you'll get from Canonic.
> 
    
```jsx
import React from "react";
import ReactDOM from "react-dom";
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";

import ContentTable from "./components/ContentTable";

import "./index.css";

const client = new ApolloClient({
  uri: "https://crm-app.can.canonic.dev/graphql",
  cache: new InMemoryCache(),
});
ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <ContentTable />
    </ApolloProvider>
  </React.StrictMode>,
  document.getElementById("root")
);
```    
src/index.js
    
- **Query the data**
    
For querying the data we will create a directory inside src called gql and create a file inside it called query.js. In it will write all the data we need from the backend.
    
```graphql
import { gql } from "@apollo/client";

export const GET_CUSTOMERS = gql`
  query {
    customers {
      name
      _id
      createdAt
      updatedAt
      email
      phone
      company
      labels
      notes {
        description
      }
    }
  }
`;
```
src/gql/query.js
    
- Propagate the API’s data.
    
Here we are propagating the data to ContentTable component. All we have to do is replace the `customer` static array we had with the code we get from API and replace all the references made to the static array with dynamic data. 
    
```jsx
import { React, useState } from "react";
import { useQuery } from "@apollo/client";
import {
  Table,
  TableBody,
  TableContainer,
  TableFooter,
  TablePagination,
  TableRow,
  Paper,
  TableCell,
  TableHead,
  CircularProgress,
} from "@mui/material";

import { GET_CUSTOMERS } from "../../gql/query";
import { TablePaginationActions } from "../Pagination";
import Modals from "../Modal";

export default function ContentTable() {
  const { data, loading } = useQuery(GET_CUSTOMERS);
  const [page, setPage] = useState(0);
  const [rowsPerPage, setRowsPerPage] = useState(5);
  const [customerData, setCustomerData] = useState();
  const [isOpen, setIsOpen] = useState();
  const emptyRows =
    page > 0
      ? Math.max(0, (1 + page) * rowsPerPage - data.customers.length)
      : 0;

  const handleChangePage = (event, newPage) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (event) => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  const handleClick = (data) => {
    setIsOpen(true);
    setCustomerData(data);
  };

  return (
    <>
      {loading && (
        <CircularProgress
          sx={{ position: "absolute", top: "50%", left: "50%" }}
        />
      )}
      {!loading && (
        <TableContainer component={Paper} sx={{ margin: "2rem", width: "95%" }}>
          <Table sx={{ minWidth: 500 }} aria-label="custom pagination table">
            <TableHead>
              <TableRow>
                <TableCell
                  align="left"
                  sx={{
                    backgroundColor: "black",
                    color: "white",
                    borderRight: "1px solid white",
                  }}
                >
                  Name
                </TableCell>
                <TableCell
                  align="left"
                  sx={{
                    backgroundColor: "black",
                    color: "white",
                    borderRight: "1px solid white",
                  }}
                >
                  Company
                </TableCell>
                <TableCell
                  align="left"
                  sx={{
                    backgroundColor: "black",
                    color: "white",
                    borderRight: "1px solid white",
                  }}
                >
                  Email
                </TableCell>
                <TableCell
                  align="left"
                  sx={{
                    backgroundColor: "black",
                    color: "white",
                    borderRight: "1px solid white",
                  }}
                >
                  Phone
                </TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {data &&
                (rowsPerPage > 0
                  ? data?.customers?.slice(
                      page * rowsPerPage,
                      page * rowsPerPage + rowsPerPage
                    )
                  : data.customers
                ).map((row, index) => (
                  <TableRow
                    key={index}
                    onClick={() => {
                      handleClick(row);
                    }}
                    sx={{ cursor: "pointer" }}
                  >
                    <TableCell
                      component="th"
                      scope="row"
                      sx={{ width: 160, borderRight: "1px solid black" }}
                    >
                      {row.name}
                    </TableCell>
                    <TableCell
                      sx={{ width: 160, borderRight: "1px solid black" }}
                      align="left"
                    >
                      {row.company}
                    </TableCell>
                    <TableCell
                      sx={{ width: 160, borderRight: "1px solid black" }}
                      align="left"
                    >
                      {row.email}
                    </TableCell>
                    <TableCell sx={{ width: 160 }} align="left">
                      {row.phone}
                    </TableCell>
                  </TableRow>
                ))}

              {emptyRows > 0 && (
                <TableRow style={{ height: 53 * emptyRows }}>
                  <TableCell colSpan={6} />
                </TableRow>
              )}
            </TableBody>
            <TableFooter>
              <TableRow>
                <TablePagination
                  rowsPerPageOptions={[5, 10, 25, { label: "All", value: -1 }]}
                  colSpan={3}
                  count={data?.customers?.length}
                  rowsPerPage={rowsPerPage}
                  page={page}
                  SelectProps={{
                    inputProps: {
                      "aria-label": "rows per page",
                    },
                    native: true,
                  }}
                  onPageChange={handleChangePage}
                  onRowsPerPageChange={handleChangeRowsPerPage}
                  ActionsComponent={TablePaginationActions}
                />
              </TableRow>
            </TableFooter>
          </Table>
          <Modals data={customerData} open={isOpen} setIsOpen={setIsOpen} />
        </TableContainer>
      )}
    </>
  );
}
```
src/components/ContentTable/ContentTable.js
    
That’s all the changes we needed to integrate Canonic’s backend. Let get on to creating the Notes component.
    
- Create Notes component
    
Before we create Notes component, let’s first prepare Modal component to display notes.
    
We will be doing these 3 things in Modal component
    
- Creating a state to hold all our notes
- Create a state to hold status on opening add a new note text box
- Create a button that can switch between Displaying the notes to adding a note
    
We will faux import Notes component before it is created and feed in the data so when we finally create the component, we will already have a data to work with it
    
```jsx
import Notes from "../Notes";
const [openAdd, setOpenAdd] = useState(false);
const [displayNotes, setDisplayNotes] = useState();
useEffect(() => {
    data && setDisplayNotes(data.notes);
}, [data]);

<Item sx={{ backgroundColor: "inherit", marginTop: "2rem" }}>
        {!openAdd && (
        <Box sx={{ flexGrow: 1 }}>
        <Paper
            elevation={1}
            sx={{
            padding: "2rem",
            height: "30rem",
            overflow: "auto",
            }}
        >
            {displayNotes?.map((note) => (
            <List>
                <ListItem sx={{ borderBottom: "1px solid black" }}>
                <ListItemText>{note.description}</ListItemText>
                </ListItem>
            </List>
            ))}
            {!openAdd && (
            <Button
                variant="outlined"
                startIcon={<AddIcon />}
                sx={{ marginTop: "1rem" }}
                onClick={() => setOpenAdd(true)}
                color="success"
            >
                Add a note
            </Button>
            )}
        </Paper>
        </Box>
    )}
    {openAdd && (
        <Notes
        setOpenAdd={setOpenAdd}
        _id={data._id}
        noteDescription={data?.notes?.map((item) => ({
            description: item.description,
        }))}
        setDisplayNotes={setDisplayNotes}
        />
    )}
```
    
After this, the Modal component shall look like this
    
```jsx
import React, { useState, useEffect } from "react";
import { styled } from "@mui/material/styles";
import {
  Typography,
  Fade,
  Modal,
  Grid,
  Paper,
  Backdrop,
  Box,
  Button,
  List,
  ListItem,
  ListItemText,
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";

import DetailsCard from "../DetailsCard";
import Notes from "../Notes";

const Item = styled(Paper)(({ theme }) => ({
  ...theme.typography.body2,
  padding: theme.spacing(1),
  textAlign: "center",
  color: theme.palette.text.secondary,
  boxShadow: "none",
}));

export default function Modals({ data, open, setIsOpen }) {
  const [openAdd, setOpenAdd] = useState(false);
  const [displayNotes, setDisplayNotes] = useState();
  const handleClose = () => {
    setIsOpen(!open);
  };
  useEffect(() => {
    data && setDisplayNotes(data.notes);
  }, [data]);
  return (
    <div>
      <Modal
        aria-labelledby="transition-modal-title"
        aria-describedby="transition-modal-description"
        open={open}
        onClose={handleClose}
        closeAfterTransition
        BackdropComponent={Backdrop}
        BackdropProps={{
          timeout: 500,
        }}
      >
        <Fade in={open}>
          <Grid
            container
            spacing={1}
            sx={{
              width: "95%",
              height: "95%",
              backgroundColor: "white",
              position: "absolute",
              top: "4%",
              left: "3%",
            }}
          >
            <Grid item xs={3} sx={{ padding: "5px" }}>
              <DetailsCard
                name={data.name}
                company={data.company}
                email={data.email}
                phone={data.phone}
                labels={data.labels}
              />
            </Grid>
            <Grid
              item
              xs={6}
              sx={{ backgroundColor: "lightgray", padding: "5px" }}
            >
              <Item sx={{ backgroundColor: "inherit", marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Notes
                </Typography>
                {!openAdd && (
                  <Box sx={{ flexGrow: 1 }}>
                    <Paper
                      elevation={1}
                      sx={{
                        padding: "2rem",
                        height: "30rem",
                        overflow: "auto",
                      }}
                    >
                      {displayNotes?.map((note) => (
                        <List>
                          <ListItem sx={{ borderBottom: "1px solid black" }}>
                            <ListItemText>{note.description}</ListItemText>
                          </ListItem>
                        </List>
                      ))}
                      {!openAdd && (
                        <Button
                          variant="outlined"
                          startIcon={<AddIcon />}
                          sx={{ marginTop: "1rem" }}
                          onClick={() => setOpenAdd(true)}
                          color="success"
                        >
                          Add a note
                        </Button>
                      )}
                    </Paper>
                  </Box>
                )}
                {openAdd && (
                  <Notes
                    setOpenAdd={setOpenAdd}
                    _id={data._id}
                    noteDescription={data?.notes?.map((item) => ({
                      description: item.description,
                    }))}
                    setDisplayNotes={setDisplayNotes}
                  />
                )}
              </Item>
            </Grid>
            <Grid item xs={3} sx={{ padding: "5px", marginTop: "2rem" }}>
              <Item>
                <Typography variant="h6" gutterBottom component="div">
                  Deals
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the revenue opportunities associated with this record
                </Typography>
              </Item>
              <Item sx={{ marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Tickets
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the customer requests associated with this record
                </Typography>
              </Item>
            </Grid>
          </Grid>
        </Fade>
      </Modal>
    </div>
  );
}
```
src/components/Modal/Modal.js
    
We just now need to add the Mutation, that way we can send the new notes to our API
    
Let’s go back to GQL directory and create a file called Mutation.js
    
```jsx
import { gql } from "@apollo/client";

export const ADD_NOTE = gql`
  mutation updateCustomerMutation($_id: ID!, $notes: [CustomerNoteInput!]!) {
    updateCustomer(_id: $_id, input: { notes: $notes }) {
      _id
      createdAt
      updatedAt
      name
      email
      phone
      company
      notes {
        description
      }
    }
  }
`;
```
src/gql/mutation.js
    
This mutations just updates the database with a new note whenever it is created.
    
Now we have Modal component conditioned, let’s begin creating Notes component. Create a directory inside components and name it Notes, create a file inside of it and call it Notes.js
    
```jsx
import { useRef } from "react";
import { useMutation } from "@apollo/client";
import { Box, TextField, Grid, Paper, Stack, Button } from "@mui/material";
import LoadingButton from "@mui/lab/LoadingButton";
import SaveIcon from "@mui/icons-material/Save";
import { ADD_NOTE } from "../../gql/mutation";
const Notes = ({ _id, noteDescription, setOpenAdd, setDisplayNotes }) => {
  const descriptionRef = useRef();
  const [addNote] = useMutation(ADD_NOTE);

  const handleClick = () => {
    let description = descriptionRef.current.value;
    if (description.replace(/\s+/g, " ").length) {
      addNote({
        variables: {
          _id,
          notes: [...noteDescription, { description }],
        },
      });
      setDisplayNotes((oldList) => [...oldList, { description }]);
      setOpenAdd(false);
    }
  };
  const handleClose = () => {
    setOpenAdd(false);
  };

  return (
    <Box sx={{ flexGrow: 1 }}>
      <Paper elevation={6} sx={{ padding: "2rem" }}>
        <Stack spacing={2}>
          <Grid container sx={{ marginTop: "1rem" }}>
            <Grid item xs={12}>
              <TextField
                id="outlined-basic"
                label="Description"
                variant="outlined"
                multiline
                rows={2}
                sx={{ width: "100%" }}
                inputRef={descriptionRef}
              />
            </Grid>
          </Grid>
          <Grid sx={{ display: "flex", justifyContent: "space-between" }}>
            <LoadingButton
              loadingPosition="start"
              startIcon={<SaveIcon />}
              variant="outlined"
              sx={{ width: "25%" }}
              onClick={handleClick}
              color="success"
            >
              Save
            </LoadingButton>
            <Button
              variant="outlined"
              color="error"
              sx={{ width: "25%" }}
              onClick={handleClose}
            >
              Close
            </Button>
          </Grid>
        </Stack>
      </Paper>
    </Box>
  );
};

export default Notes;
```
src/components/Notes/Notes.js
    
In here while updating the database with mutation, we are also updating `displayNotes` state, so the component would refresh and new note will be visible immediately
    
The end result should look like this
    
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x2tzmb5u5hfnf0c1ifzl.png)
 
- Create Deals componenet.

We are going to have a drop here where the user could set any deal to the customer, the data will get updated the database.

Let’s begin by defining a mutation for it. Navigate back to mutation.js that resides in gql directory.

```jsx
import { gql } from "@apollo/client";

export const ADD_NOTE = gql`
  mutation updateCustomerMutation($_id: ID!, $notes: [CustomerNoteInput!]!) {
    updateCustomer(_id: $_id, input: { notes: $notes }) {
      _id
      createdAt
      updatedAt
      name
      email
      phone
      company
      notes {
        description
      }
    }
  }
`;

export const ADD_DEAL = gql`
  mutation updateDealsMutation($_id: ID!, $deals: [ID!]!) {
    updateDeal(_id: $_id, input: { deals: $deals }) {
      title
      amount
      dealOwner
      deals {
        _id
        name
        company
        phone
        email
      }
    }
  }
`;
```
src/gql/mutation.js

We don’t need much from Modal in this case so let begin by creating Deals component, create a directory inside components name it Deals, create a file inside it and name it Deals.js

The flow is pretty much same as Notes in here, We are expecting a `_id` prop from Modal here.

Lets pass it

```jsx
import React, { useState } from "react";
import { styled } from "@mui/material/styles";
import {
  Typography,
  Fade,
  Modal,
  Grid,
  Paper,
  Backdrop,
  Box,
  Button,
  List,
  ListItem,
  ListItemText,
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";

import DetailsCard from "../DetailsCard";
import Notes from "../Notes";
import Deals from "../Deals";

const Item = styled(Paper)(({ theme }) => ({
  ...theme.typography.body2,
  padding: theme.spacing(1),
  textAlign: "center",
  color: theme.palette.text.secondary,
  boxShadow: "none",
}));

export default function Modals({ data, open, setIsOpen }) {
  const [openAdd, setOpenAdd] = useState(false);
  const [displayNotes, setDisplayNotes] = useState();
  const handleClose = () => {
    setIsOpen(!open);
  };

  return (
    <div>
      <Modal
        aria-labelledby="transition-modal-title"
        aria-describedby="transition-modal-description"
        open={open}
        onClose={handleClose}
        closeAfterTransition
        BackdropComponent={Backdrop}
        BackdropProps={{
          timeout: 500,
        }}
      >
        <Fade in={open}>
          <Grid
            container
            spacing={1}
            sx={{
              width: "95%",
              height: "95%",
              backgroundColor: "white",
              position: "absolute",
              top: "4%",
              left: "3%",
            }}
          >
            <Grid item xs={3} sx={{ padding: "5px" }}>
              <DetailsCard
                name={data.name}
                company={data.company}
                email={data.email}
                phone={data.phone}
                labels={data.labels}
              />
            </Grid>
            <Grid
              item
              xs={6}
              sx={{ backgroundColor: "lightgray", padding: "5px" }}
            >
              <Item sx={{ backgroundColor: "inherit", marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Notes
                </Typography>
                {!openAdd && (
                  <Box sx={{ flexGrow: 1 }}>
                    <Paper
                      elevation={1}
                      sx={{
                        padding: "2rem",
                        height: "30rem",
                        overflow: "auto",
                      }}
                    >
                      {displayNotes?.map((note) => (
                        <List>
                          <ListItem sx={{ borderBottom: "1px solid black" }}>
                            <ListItemText>{note.description}</ListItemText>
                          </ListItem>
                        </List>
                      ))}
                      {!openAdd && (
                        <Button
                          variant="outlined"
                          startIcon={<AddIcon />}
                          sx={{ marginTop: "1rem" }}
                          onClick={() => setOpenAdd(true)}
                          color="success"
                        >
                          Add a note
                        </Button>
                      )}
                    </Paper>
                  </Box>
                )}
                {openAdd && (
                  <Notes
                    setOpenAdd={setOpenAdd}
                    _id={data._id}
                    noteDescription={data?.notes?.map((item) => ({
                      description: item.description,
                    }))}
                    setDisplayNotes={setDisplayNotes}
                  />
                )}
              </Item>
            </Grid>
            <Grid item xs={3} sx={{ padding: "5px", marginTop: "2rem" }}>
              <Item>
                <Typography variant="h6" gutterBottom component="div">
                  Deals
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the revenue opportunities associated with this record
                </Typography>
                <Deals userId={data._id} />
              </Item>
              <Item sx={{ marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Tickets
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the customer requests associated with this record
                </Typography>
              </Item>
            </Grid>
          </Grid>
        </Fade>
      </Modal>
    </div>
  );
}
```
src/components/Modal/Modal.js

And, our Deals component is done. It should look like this

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pizcim1vb8btj7ph88n4.png) 

- Create a Tickets component
    
This component will make a POST request to Asana API whenever a user inputs a title for the ticket, we are using create a task API for this you can read more about it [here](https://developers.asana.com/docs/create-a-task)
    
It requires `Title` and either `workspace` or `projects` or `parent` defined in order to create a task
    
```jsx
import { useRef, useState } from "react";
import { Box, TextField, Grid, Paper, Stack, Button } from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import DoneIcon from "@mui/icons-material/Done";

const Tickets = () => {
  const [addTicket, setAddTicket] = useState(false);
  const [addedTicketStatus, setAddedTicketStatus] = useState(false);
  const ticketNameRef = useRef();
  const handleAdd = () => {
    let data = {
      data: {
        name: ticketNameRef.current.value,
        workspace: process.env.REACT_APP_ASANA_WORKSPACE,
      },
    };
    if (!addedTicketStatus)
      fetch("https://app.asana.com/api/1.0/tasks", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: process.env.REACT_APP_ASANA_KEY,
        },
        body: JSON.stringify(data),
      })
        .then((res) => res.json())
        .then(() => setAddedTicketStatus(true));
  };
  const handleClose = () => {
    setAddTicket(false);
    setAddedTicketStatus(false);
  };
  return (
    <Box sx={{ flexGrow: 1 }}>
      {!addTicket && (
        <Button
          variant="outlined"
          color="success"
          onClick={() => setAddTicket(true)}
        >
          Add a ticket to Asana
        </Button>
      )}
      {addTicket && (
        <Paper elevation={6} sx={{ padding: "2rem" }}>
          <Stack spacing={2}>
            <Grid container sx={{ marginTop: "1rem" }}>
              <Grid item xs={12}>
                <TextField
                  id="outlined-basic"
                  label="Ticket name"
                  variant="outlined"
                  sx={{ width: "100%" }}
                  inputRef={ticketNameRef}
                />
              </Grid>
            </Grid>
            <Grid sx={{ display: "flex", justifyContent: "space-between" }}>
              <Button
                startIcon={addedTicketStatus ? <DoneIcon /> : <AddIcon />}
                variant="outlined"
                color="success"
                onClick={handleAdd}
                disabled={addedTicketStatus}
              >
                {addedTicketStatus ? "Added" : "Add"}
              </Button>
              <Button variant="outlined" color="error" onClick={handleClose}>
                Close
              </Button>
            </Grid>
          </Stack>
        </Paper>
      )}
    </Box>
  );
};

export default Tickets;
```
src/components/Tickets/Tickets.js
    
We don’t need any props to be passed from Modal in Ticket component, so let’s just import it
    
```jsx
import React, { useState } from "react";
import { styled } from "@mui/material/styles";
import {
  Typography,
  Fade,
  Modal,
  Grid,
  Paper,
  Backdrop,
  Box,
  Button,
  List,
  ListItem,
  ListItemText,
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";

import DetailsCard from "../DetailsCard";
import Notes from "../Notes";
import Deals from "../Deals";
import Tickets from "../Tickets";

const Item = styled(Paper)(({ theme }) => ({
  ...theme.typography.body2,
  padding: theme.spacing(1),
  textAlign: "center",
  color: theme.palette.text.secondary,
  boxShadow: "none",
}));

export default function Modals({ data, open, setIsOpen }) {
  const [openAdd, setOpenAdd] = useState(false);
  const [displayNotes, setDisplayNotes] = useState();
  const handleClose = () => {
    setIsOpen(!open);
  };

  return (
    <div>
      <Modal
        aria-labelledby="transition-modal-title"
        aria-describedby="transition-modal-description"
        open={open}
        onClose={handleClose}
        closeAfterTransition
        BackdropComponent={Backdrop}
        BackdropProps={{
          timeout: 500,
        }}
      >
        <Fade in={open}>
          <Grid
            container
            spacing={1}
            sx={{
              width: "95%",
              height: "95%",
              backgroundColor: "white",
              position: "absolute",
              top: "4%",
              left: "3%",
            }}
          >
            <Grid item xs={3} sx={{ padding: "5px" }}>
              <DetailsCard
                name={data.name}
                company={data.company}
                email={data.email}
                phone={data.phone}
                labels={data.labels}
              />
            </Grid>
            <Grid
              item
              xs={6}
              sx={{ backgroundColor: "lightgray", padding: "5px" }}
            >
              <Item sx={{ backgroundColor: "inherit", marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Notes
                </Typography>
                {!openAdd && (
                  <Box sx={{ flexGrow: 1 }}>
                    <Paper
                      elevation={1}
                      sx={{
                        padding: "2rem",
                        height: "30rem",
                        overflow: "auto",
                      }}
                    >
                      {displayNotes?.map((note) => (
                        <List>
                          <ListItem sx={{ borderBottom: "1px solid black" }}>
                            <ListItemText>{note.description}</ListItemText>
                          </ListItem>
                        </List>
                      ))}
                      {!openAdd && (
                        <Button
                          variant="outlined"
                          startIcon={<AddIcon />}
                          sx={{ marginTop: "1rem" }}
                          onClick={() => setOpenAdd(true)}
                          color="success"
                        >
                          Add a note
                        </Button>
                      )}
                    </Paper>
                  </Box>
                )}
                {openAdd && (
                  <Notes
                    setOpenAdd={setOpenAdd}
                    _id={data._id}
                    noteDescription={data?.notes?.map((item) => ({
                      description: item.description,
                    }))}
                    setDisplayNotes={setDisplayNotes}
                  />
                )}
              </Item>
            </Grid>
            <Grid item xs={3} sx={{ padding: "5px", marginTop: "2rem" }}>
              <Item>
                <Typography variant="h6" gutterBottom component="div">
                  Deals
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the revenue opportunities associated with this record
                </Typography>
                <Deals userId={data._id} />
              </Item>
              <Item sx={{ marginTop: "2rem" }}>
                <Typography variant="h6" gutterBottom component="div">
                  Tickets
                </Typography>
                <Typography variant="p" gutterBottom component="div">
                  Track the customer requests associated with this record
                </Typography>
                <Tickets />
              </Item>
            </Grid>
          </Grid>
        </Fade>
      </Modal>
    </div>
  );
}
```
src/component/Modal/Modal.js
    
Now we have Tickets component ready, it should look like this
    
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3dita6hb3w1739t83mw3.png) 
    
- Create a Header component.
    
This will be a simple App bar that will display the a bar at the top of the page with the title of our project.
    
Let’s create a directory inside components and name it Header and a create file inside it and name Header.js
    
```jsx
    import React from "react";

import { AppBar, Box, Toolbar, Typography } from "@mui/material";

const Header = () => {
  return (
    <Box sx={{ flexGrow: 1 }}>
      <AppBar position="static" sx={{ backgroundColor: "#000000" }}>
        <Toolbar>
          <Typography
            variant="h6"
            noWrap
            component="div"
            sx={{
              flexGrow: 1,
              display: { xs: "none", sm: "block" },
              textAlign: "center",
            }}
          >
            CRM
          </Typography>
        </Toolbar>
      </AppBar>
    </Box>
  );
};

export default Header;
```
src/components/Header/Header.js
    
All we have to do now is to import it in index.js file 

```jsx
import React from "react";
import ReactDOM from "react-dom";
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";

import ContentTable from "./components/ContentTable";
import Header from "./components/Header";
import "./index.css";

const client = new ApolloClient({
  uri: "https://crm-app.can.canonic.dev/graphql",
  cache: new InMemoryCache(),
});
ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <Header />
      <ContentTable />
    </ApolloProvider>
  </React.StrictMode>,
  document.getElementById("root")
);
```
src/index.js

Voila! we are done.

[sample code link](https://github.com/canonic-dev/canonic-samples/tree/main/crm-app). 

---

**BONUS**

**You can create log all the customer deals on Google Sheet.**

Canonic has Google Sheet’s integrations, so all you have to do is open your cloned project, navigate to API,  there select Deals table, Click on DB Triggers, there you will find a predefined Database trigger called `UpdateTheSheet` Click on Google Sheets webhook. You will have to authenticate yourself with a Google account. Then go to `required` and change the SpreadsheetID with your spreadsheet, make sure it is associated with the Google account you authenticated yourself with. You can change the range if you want. And, that’s it, you have successfully integrated Google Sheets into your CRM app now whenever user create a deal for a customer, DB trigger will update the Google Sheet. 

Conclusion: It was a long journey well, I hope it was worth it. At the end you you learned how to created a feature packed awesome app, If you want, you can also duplicate this project from Canonic's sample app and easily get started by customizing it as per your experience. Check it out [app.canonic.dev](http://app.canonic.dev/).

You can also check out our other guides [here](https://dev.to/canonic).

Join us on discord to discuss or share with our community. Write to us for any support requests at [support@canonic.dev](mailto:support@canonic.dev). Check out our [website](http://www.canonic.dev/) to know more about Canonic.