React App - sandeepganapatimore/webatool GitHub Wiki

React App

installation

npm install react-router-dom
npm install react-chartjs-2
npm install @mui/material
npm install @mui/x-data-grid-generator

File Structure

Home/
  Header.js
  AppInfo.js
  Home.js
  Layout.js
  Main.js
  TestUrl.js
  VisualSection.js

DashBoard/
  Dashboard.js
  Layout.js
  ListItems.js
  Navbar.js


Scan/
  Chart.js
  IssueDetails.js
  newScans.js
  ScansDetails.js
  Scans.js

utils/
  helper.js

Component/
  Copy/
   index.js
  Forms/
   index.js

Auth/
  signup.

ContextState.js
App.js

Let's see how we have used react js as our frontend framework for our project. We will go by the above folder structure.

Home

Header.js

  • This component represents the header of our Home page.

  • In which it includes Logo name and the list of the elements such as dashboard login docs which are the navigating list element.

  • Create Array of objects with entity name and link.

  • name entity for displaying the field and link entity for navigating after clicking on the entity name.

const pages = [
  { name: "Documentation", link: "/docs" },
  { name: "Dashboard", link: "/dashboard" },
  { name: "Login", link: "/user/signin" },
];

Mobile responsiveness

  • useState() is a React Hook that lets you add a state variable to your component.
  • initialize the anchorElNav with useState().
  • This state is used to the state management of the menubar event.
  • menubar event is used in mobile responsiveness.
  const [anchorElNav, setAnchorElNav] = React.useState(null);
  • handleOpenNavMenu is the function for event management purpose. This function takes an event as an parameter set this event in setAnchorElNav as the currentTarget.
  const handleOpenNavMenu = (event) => {
    setAnchorElNav(event.currentTarget);
  };
  • handleCloseNavMenu is the function to close the menu bar which opened.For this we have to remove the event from the state.
  const handleCloseNavMenu = () => {
    setAnchorElNav(null);
  };
  • Now it's time to return the jsx from the component.return the jsx inside the fragments.

  • Box import Box from material UI and apply the flexGrow and display css method to fit the element to the screen.

      <Box sx={{ flexGrow: 2, display: { xs: "flex", md: "none" } }}>
  • IconButton and MenuIcon import this elements from the react mui. IconButton has handleOpenNavMenu state. which is defined for handling the state for opening and closing the menu bar.
  • Inside the IconButton render MenuIcon.
    <IconButton
          size="large"
          aria-label="account of current user"
          aria-controls="menu-appbar"
          aria-haspopup="true"
          onClick={handleOpenNavMenu}
          sx={{ color: "#1d1b1e" }}
        >
          <MenuIcon />
        </IconButton>
  • Menu Through this simple dialogue can be opened displaying the details regarding the menu items.
  • Inside this menu list will be rendered.
   <Menu
          id="menu-appbar"
          anchorEl={anchorElNav}
          anchorOrigin={{
            vertical: "bottom",
            horizontal: "left",
          }}
          keepMounted
          transformOrigin={{
            vertical: "top",
            horizontal: "left",
          }}
          open={Boolean(anchorElNav)}
          onClose={handleCloseNavMenu}
          sx={{
            color: "#1d1b1e",
            display: { xs: "block", md: "none" },
          }}
        >
          {pages.map((page) => (
            <MenuItem key={page?.name} onClick={handleCloseNavMenu}>
              <Typography
                component="a"
                sx={{ textDecoration: "none" }}
                textAlign="center"
                href={page.link}
              >
                {page?.name}
              </Typography>
            </MenuItem>
          ))}
        </Menu>

Web responsiveness

  • Box,Typography,List map the page object inside the List component.
function HeaderWeb() {
  return (
    <Box sx={{ display: { xs: "none", md: "flex" }, ml: 5 }}>
      <Typography
        variant="h6"
        noWrap
        component="a"
        href="/"
        sx={{ textDecoration: "none", mt: 1 }}
      >
        WEBATOOL
      </Typography>

      <List
        sx={{
          display: "flex",
          flexDirection: "row",
          padding: 0,
        }}
      >
        {pages.map((page) => (
          <ListItemButton key={page?.name} href={page.link}>
            <ListItemText primary={page.name} sx={{ color: "#121211" }} />
          </ListItemButton>
        ))}
      </List>
    </Box>
  );
}
  • AppBar which is the mui component for the navbar.
  • container it is also an mui component.
  • ToolBar it is used for rendering the elements inside the AppBar component.
export default function Header() {
  return (
    <AppBar position="fixed" sx={{ backgroundColor: "#FFFFFF", boxShadow: 0 }}>
      <Container disableGutters>
        <Toolbar>
          <HeaderWeb />
          <HeaderMobile />
        </Toolbar>
      </Container>
    </AppBar>
  );
}

Alt text

Code All Together.

import * as React from "react";
import AppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";
import Container from "@mui/material/Container";
import List from "@mui/material/List";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Box from "@mui/material/Box";
import IconButton from "@mui/material/IconButton";
import Menu from "@mui/material/Menu";
import MenuIcon from "@mui/icons-material/Menu";
import MenuItem from "@mui/material/MenuItem";

const pages = [
  { name: "Documentation", link: "/docs" },
  { name: "Dashboard", link: "/dashboard" },
  { name: "Login", link: "/user/signin" },
];

function HeaderMobile() {
  const [anchorElNav, setAnchorElNav] = React.useState(null);

  const handleOpenNavMenu = (event) => {
    setAnchorElNav(event.currentTarget);
  };

  const handleCloseNavMenu = () => {
    setAnchorElNav(null);
  };

  return (
    <>
      <Box sx={{ flexGrow: 2, display: { xs: "flex", md: "none" } }}>
        <IconButton
          size="large"
          aria-label="account of current user"
          aria-controls="menu-appbar"
          aria-haspopup="true"
          onClick={handleOpenNavMenu}
          sx={{ color: "#1d1b1e" }}
        >
          <MenuIcon />
        </IconButton>
        <Menu
          id="menu-appbar"
          anchorEl={anchorElNav}
          anchorOrigin={{
            vertical: "bottom",
            horizontal: "left",
          }}
          keepMounted
          transformOrigin={{
            vertical: "top",
            horizontal: "left",
          }}
          open={Boolean(anchorElNav)}
          onClose={handleCloseNavMenu}
          sx={{
            color: "#1d1b1e",
            display: { xs: "block", md: "none" },
          }}
        >
          {pages.map((page) => (
            <MenuItem key={page?.name} onClick={handleCloseNavMenu}>
              <Typography
                component="a"
                sx={{ textDecoration: "none" }}
                textAlign="center"
                href={page.link}
              >
                {page?.name}
              </Typography>
            </MenuItem>
          ))}
        </Menu>
      </Box>
    </>
  );
}

function HeaderWeb() {
  return (
 <Box sx={{ display: { xs: "none", md: "flex" }, ml: 5 }}>
      <Typography
        variant="h6"
        noWrap
        component="a"
        href="/"
        sx={{ textDecoration: "none", mt: 1 }}
      >
        WEBATOOL
      </Typography>

      <List
        sx={{
          display: "flex",
          flexDirection: "row",
          padding: 0,
        }}
      >
        {pages.map((page) => (
          <ListItemButton key={page?.name} href={page.link}>
            <ListItemText primary={page.name} sx={{ color: "#121211" }} />
          </ListItemButton>
        ))}
      </List>
    </Box>
  );
}

export default function Header() {
  return (
    <AppBar position="fixed" sx={{ backgroundColor: "#FFFFFF", boxShadow: 0 }}>
      <Container disableGutters>
        <Toolbar>
          <HeaderWeb />
          <HeaderMobile />
        </Toolbar>
      </Container>
    </AppBar>
  );
}

AppInfo.js

  • The component represents the plain text which gives brief idea about our application.

Mobile responsiveness.

  • Box,Typography for this apply style display xs as flex.
  • Inside Box implement extra use Typography three times.In inner box with different font color.
<Box
        sx={{ display: { xs: "flex", md: "none" }, flexDirection: "column" }}
      >
        <Box sx={{ textAlign: "center" }}>
          <Typography variant="h4" color="#1d1b1e">
            Check Accessibility
          </Typography>
          <Typography variant="h4" sx={{ color: "#6D28D9" }}>
            of your Web Application here
          </Typography>
        </Box>
        <Typography variant="h6" sx={{ color: "#1d1b1e", mt: 1 }}>
          WebATool is an Web Accessibility Evaluation Tool for testing
          application like websites and other HTML based User interfaces and
          designed using an Axe Core Engine
        </Typography>
      </Box>

Web Responsiveness.

  • Box,Typography for this apply style display md as flex.
  • Inside Box implement extra use Typography three times.In inner box with different font color. Alt text

Code all together

import React from "react";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";

export default function AppInfo() {
  return (
    <>
      <Box
        sx={{ display: { xs: "flex", md: "none" }, flexDirection: "column" }}
      >
        <Box sx={{ textAlign: "center" }}>
          <Typography variant="h4" color="#1d1b1e">
            Check Accessibility
          </Typography>
          <Typography variant="h4" sx={{ color: "#6D28D9" }}>
            of your Web Application here
          </Typography>
        </Box>
        <Typography variant="h6" sx={{ color: "#1d1b1e", mt: 1 }}>
          WebATool is an Web Accessibility Evaluation Tool for testing
          application like websites and other HTML based User interfaces and
          designed using an Axe Core Engine
        </Typography>
      </Box>
{/* for web responsiveness*/}
      <Box
        sx={{ display: { xs: "none", md: "flex" }, flexDirection: "column" }}
      >
        <Box sx={{ textAlign: "center" }}>
          <Typography variant="h2" color="#1d1b1e">
            Check Accessibility
          </Typography>
          <Typography variant="h2" sx={{ color: "#6D28D9" }}>
            of your Web Application here
          </Typography>
        </Box>
        <Typography
          variant="h6"
          sx={{
            color: "#1d1b1e",
            mt: 1,
            textAlign: "center",
          }}
        >
          WebATool is an Web Accessibility Evaluation Tool for testing
          application like
        </Typography>
        <Typography
          variant="h6"
          sx={{
            color: "#1d1b1e",
            textAlign: "center",
          }}
        >
          websites and other HTML based User interfaces and designed using an
          Axe Core Engine
        </Typography>
      </Box>
    </>
  );
}

TestUrl.js

  • This component represents the input box which accepts the url from the user for analyzing purpose.

  • Circular progress icon get displayed until the web accessibility is under the process.

  • After the completion of analysis of the url is navigates to the Dashboard section where the issues of the url are shown.

  • create the state for storing url by using useState().

 const [url, setUrl] = useState("");
  • loading state is for handling the loading state.
  const [loading, setLoading] = useState();
  • The useNavigate hook returns an imperative method that you can use for changing location.
  • Create instance from the useNavigate() method.
  const navigate = useNavigate();
  • Create an function using useCallback hook. inside which use fetch method for fetching the api.
  • Give the dependencies for the useCallback hook for which state has to be change.
  • Inside the fetch function. apply the method as POST for posting user entered URL to the server.
  • Pass the data from the body.
 const handleCallback = useCallback(() => {
    setLoading(true);
    fetch("http://localhost:8000/api/scans/new", {
      method: "POST",
      body: JSON.stringify({
        url: url,
      }),
      headers: {
        "Content-type": "application/json; charset=UTF-8",
      },
    })
      .then((response) => response.json())
      .then((data) => {
        if (!data?.success) {
          setError(data?.error);
        } else {
          navigate(`/scans/${data?.data?.scanId}`);
        }
        setLoading(false);
      });
  }, [navigate, setError, url]);
  • Render the from the "../form/FormController".
  • pass the props such as url,setUrl,validUrl,handleCallBack.
  • import validUrl method "../utils/helper" it is the function to check whether entered data is valid url or not.
  <Box
        sx={{
          display: { xs: "none", md: "flex" },
          flexDirection: "row",
          justifyContent: "center",
          mt: 2,
        }}
      >

        <FormController
          setUrl={setUrl}
          url={url}
          validUrl={validUrl}
          handleCallback={handleCallback}
          width="100%"
          buttonName="Test Url"
        />
  • For loading the component take a state as boolean.
  • Apply conditional rendering method for the component for loading.
  {loading && (
        <Box
          sx={{
            display: { xs: "none", md: "flex" },
            mt: 2,
            justifyContent: "center",
            height: "80px",
            width: "100%",
            // mr:10
          }}
        >
          <Box
            sx={{
              display: { xs: "none", md: "flex" },
              alignItems: "center",
              backgroundColor: "purple",
              justifyContent: "center",
              p: 1,
              width: "65%",
              borderRadius: 1,
              mt: 2,
              color: "#FFFFFF",
              mr: 4,
              pl: 2,
            }}
          >
            <CircularProgress sx={{ mr: 2, color: "#FFFFFF" }} />{" "}
            <span> {"  "} Processing....</span>
          </Box>
        </Box>
      )}

Alt text

Code all together

import React, { useCallback, useState } from "react";
import CircularProgress from "@mui/material/CircularProgress";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import OutlinedInput from "@mui/material/OutlinedInput";
import FormControl from "@mui/material/FormControl";
import { useNavigate } from "react-router-dom";
import FormController from '../Components/Forms/FormController';

import { validUrl } from "../utils/helper";

export default function TestUrl(props) {
  const { setError } = props;
  // console.log("setError", setError);
  const [url, setUrl] = useState("");
  const [loading, setLoading] = useState();
  const navigate = useNavigate();

  const handleCallback = useCallback(() => {
    setLoading(true);
    fetch("http://localhost:8000/api/scans/new", {
      method: "POST",
      body: JSON.stringify({
        url: url,
      }),
      headers: {
        "Content-type": "application/json; charset=UTF-8",
      },
    })
      .then((response) => response.json())
      .then((data) => {
        if (!data?.success) {
          setError(data?.error);
        } else {
          navigate(`/scans/${data?.data?.scanId}`);
        }
        setLoading(false);
      });
  }, [navigate, setError, url]);

  return (
    <>
      <Box
        sx={{
          display: { xs: "none", md: "flex" },
          flexDirection: "row",
          justifyContent: "center",
          mt: 2,
        }}
      >

        <FormController
          setUrl={setUrl}
          url={url}
          validUrl={validUrl}
          handleCallback={handleCallback}
          width="100%"
          buttonName="Test Url"
        />
      </Box>
      {loading && (
        <Box
          sx={{
            display: "flex",
            mt: 2,
            justifyContent: "center",
            height: "80px",
            width: "100%",
            // mr:10
          }}
        >
          <Box
            sx={{
              display: "flex",
              alignItems: "center",
              backgroundColor: "purple",
              justifyContent: "center",
              p: 1,
              width: "65%",
              borderRadius: 1,
              mt: 2,
              color: "#FFFFFF",
              mr: 4,
              pl: 2,
            }}
          >
            <CircularProgress sx={{ mr: 2, color: "#FFFFFF" }} />{" "}
            <span> {"  "} Processing....</span>
          </Box>
        </Box>
      )}
      {/* </Box> */}
    </>
  );
}

VisualSection.js

  • This section of the component gives the visual representation of the application.
  • we have applied here Grid css
    <Grid
        container
        sx={({ textAlign: "center" }, { display: { xs: "flex", md: "none" } })}
      >
</Grid>
  • Make grid of two sections where Visual Analytics Reports text typography goes in one section and image related to dashboard goes in another section.
        <Grid item xs={12} sx={{ mt: 2 }}>
          <Box sx={{ textAlign: "center" }}>
            <Typography variant="h4" sx={{ color: color }}>
              Visual
            </Typography>
            <br />
            <Typography variant="h4" sx={{ color: color }}>
              Analytics Reports
            </Typography>
          </Box>
        </Grid>


        <Grid item xs={12} sx={{ mt: 2 }}>
          <img
            src={WebATool}
            alt="webatool"
            width="100%"
            height="auto"
            loading="lazy"
          />
        </Grid>
  • Apply display flex for mobile responsiveness and for web responsiveness.

Code All together.

Alt text

import React from "react";
import Typography from "@mui/material/Typography";
import WebATool from "./webAttol.png";
import Box from "@mui/material/Box";
import { Grid } from "@mui/material";
const color = "#6D28D9";
export default function VisualSection() {
  return (
    <>
      <Grid
        container
        sx={({ textAlign: "center" }, { display: { xs: "flex", md: "none" } })}
      >
        <Grid item xs={12} sx={{ mt: 2 }}>
          <Box sx={{ textAlign: "center" }}>
            <Typography variant="h4" sx={{ color: color }}>
              Visual
            </Typography>
            <br />
            <Typography variant="h4" sx={{ color: color }}>
              Analytics Reports
            </Typography>
          </Box>
        </Grid>

        <Grid item xs={12} sx={{ mt: 2 }}>
          <img
            src={WebATool}
            alt="webatool"
            width="100%"
            height="auto"
            loading="lazy"
          />
        </Grid>
      </Grid>

      {/* Weblayer */}
      <Grid
        container
        sx={
          ({ textAlign: "center" },
          { display: { xs: "none", md: "flex" }, mt: 1 })
        }
      >
        <Grid item xs={6} sx={{ mt: 2 }}>
          <Box sx={{ textAlign: "center" }}>
            <Typography variant="h2" sx={{ color: color }}>
              Visual
            </Typography>
            <br />
            <Typography variant="h2" sx={{ color: color }}>
              Analytics Reports
            </Typography>
          </Box>
        </Grid>

        <Grid item xs={6} sx={{ mt: 1 }}>
          <img
            src={WebATool}
            alt="Visual Analytics Reports"
            width="100%"
            height="auto"
            loading="lazy"
          />
        </Grid>
      </Grid>
    </>
  );
}

Main.js

  • Some users intentionally or unintentionally passes invalid url or some kind of string to the input box.
  • So rather than hitting the wrong data to the database we can just verify there whether it is valid url or not.
  • Create an state using useState to catch the error.
  const [error, setError] = useState(undefined);
  • Define an handle function to update the error state.
  const handleClose = () => {
    setError(undefined);
  };
  • import the Box from the material apply parent element as Box while returning javascript.
<Box>
<Box  sx={{ display: "flex", flexDirection: "column" }}>
</Box>
</Box>
  • import the component from current feature. such as ToolInfo,TestUrl,VisualSection.
<Box  sx={{ display: "flex", flexDirection: "column" }}>
    <ToolInfo />
    <TestUrl setError={setError} />
    <VisualSection />
</Box>
  • Define the Alert function to show alert message. using the forwardRef(()=>{}) which is used for exposing the Dom to parent node.
  • use SnackBar component from the material to display popup model error whenever user enter value other than valid valid url.
  • for showing the error render Alert component to display data.
  • Apply conditional rendering to display this component.
  • Call handleClose function inside the Alert component.
       {error !== undefined && (
          <Snackbar
            anchorOrigin={{ vertical: "top", horizontal: "right" }}
            open={error !== undefined}
            autoHideDuration={6000}
            onClose={handleClose}
          >
            <Alert
              onClose={handleClose}
              severity="error"
              sx={{ width: "100%" }}
            >
              {error}
            </Alert>
          </Snackbar>
        )}

Code all together

import React, { useState } from "react";

import Box from "@mui/material/Box";
import ToolInfo from "./AppInfo";
import TestUrl from "./TestUrl";
import VisualSection from "./VisualSection";
import Snackbar from "@mui/material/Snackbar";
import MuiAlert from "@mui/material/Alert";
const Alert = React.forwardRef(function Alert(props, ref) {
  return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />;
});

export default function Main() {
  const [error, setError] = useState(undefined);
  const handleClose = () => {
    setError(undefined);
  };
  return (
    <Box>
      <Box sx={{ display: "flex", flexDirection: "column" }}>
        <ToolInfo />
        <TestUrl setError={setError} />
        <VisualSection />

        {error !== undefined && (
          <Snackbar
            anchorOrigin={{ vertical: "top", horizontal: "right" }}
            open={error !== undefined}
            autoHideDuration={6000}
            onClose={handleClose}
          >
            <Alert
              onClose={handleClose}
              severity="error"
              sx={{ width: "100%" }}
            >
              {error}
            </Alert>
          </Snackbar>
        )}
      </Box>
    </Box>
  );
}

layout.js

  • The layout is the parent component of the main component.
  • import the header.
  • first render the Header component and then render it's children.
  • The children is component.
import { Box } from "@mui/system";
import Header from "./Header";
export default function Layout({ children }) {
  return (
    <div>
      <Header />
      <Box sx={{ mt: 10 }}>{children}</Box>
    </div>
  );
}

Alt text

Home.js

  • import given components.
  • All the child components are brought in this component so that we can export our home page feature bunch in single component to app.js
import React from "react";
import Main from "./Main";
import Layout from "./Layout";
import Container from "@mui/system/Container";
import Typography from "@mui/material/Typography";

function Home() {
  return (
    <Container>
      <Layout>
      // Below components are the children of the layout components
        <Main />
        <Typography variant="body2" sx={{ mt: 2, textAlign: "center" }}>
          Copy Right @2023
        </Typography>
      </Layout>
    </Container>
  );
}

export default Home;

Here we finished the Home page component features

Not let's move towards the DashBoard.

DashBoard

Navbar.js

  • This component represent the Header of our DashBoard.
  • import the AppBar from the MUI.
  <AppBar
      position="absolute"
      sx={{ backgroundColor: "#fffbff", boxShadow: "none" }}
    >
  </AppBar>
  • import the ToolBar from MUI and render it inside the AppBar.
     <Toolbar>
        <Typography
          variant="h6"
          component="a"
          href="/"
          sx={{ textDecoration: "none" }}
          color="#1d1b1e"
        >
          WEBATOOL
        </Typography>
      </Toolbar>

Alt text

Code AllTogether

import * as React from "react";
import AppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";

export default function DashBoardNavBar() {
  return (
    <AppBar
      position="absolute"
      sx={{ backgroundColor: "#fffbff", boxShadow: "none" }}
    >
      <Toolbar>
        <Typography
          variant="h6"
          component="a"
          href="/"
          sx={{ textDecoration: "none" }}
          color="#1d1b1e"
        >
          WEBATOOL
        </Typography>
      </Toolbar>
    </AppBar>
  );
}

ListItems.js

  • On the left side of the dashboard page we represent three list icons with there name.
  • ListItem such as DashBoard newScans Scans.
  • This ListItems are Navigatable.
  • Create the color constant variable and array of objects.
  • listConfig array object consist of path,name and color entity.
const color = "#1d1b1e";
const listConfig = [
  {
    path: "/dashboard",
    name: "DashBoard",
    color,
  },
  {
    path: "/scans",
    name: "Scans",
    color,
  },
  {
    path: "/",
    name: "New Scans",
    color,
  },
];
  • Create an function named as GetIcon. Inside which use switch case parameter as name to display the icon as per there names.
function GetIcons(name) {
  switch (name) {
    case "DashBoard":
      return <DashboardIcon sx={{ color: "#1d1b1e" }} />;
    case "Scans":
      return <DashboardOutlinedIcon sx={{ color: "#1d1b1e" }} />;
    case "New Scans":
      return <DashboardCustomizeRoundedIcon sx={{ color: "#1d1b1e" }} />;
    default:
      return <></>;
  }
}
  • import List,ListItemButton,ListItemIcon,ListItemText from react material UI.
  <List component="nav" sx={{ mr: 10, mt: 2 }}>
      {listConfig?.map((item) => {
        return (
          <ListItemButton href={item?.path} key={item?.name}>
            <ListItemIcon sx={{ color: item?.color }}>
              {GetIcons(item?.name)}
            </ListItemIcon>
            <ListItemText
              primary={item?.name}
              sx={{ color: item?.color, display: { xs: "none", md: "flex" } }}
            />
          </ListItemButton>
        );
      })}
    </List>

Alt text

Code AllTogether

import * as React from "react";
import List from "@mui/material/List";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import DashboardIcon from "@mui/icons-material/Dashboard";
import DashboardOutlinedIcon from "@mui/icons-material/DashboardOutlined";
import DashboardCustomizeRoundedIcon from "@mui/icons-material/DashboardCustomizeRounded";

const color = "#1d1b1e";
const listConfig = [
  {
    path: "/dashboard",
    name: "DashBoard",
    color,
  },
  {
    path: "/scans",
    name: "Scans",
    color,
  },
  {
    path: "/",
    name: "New Scans",
    color,
  },
];

function GetIcons(name) {
  switch (name) {
    case "DashBoard":
      return <DashboardIcon sx={{ color: "#1d1b1e" }} />;
    case "Scans":
      return <DashboardOutlinedIcon sx={{ color: "#1d1b1e" }} />;
    case "New Scans":
      return <DashboardCustomizeRoundedIcon sx={{ color: "#1d1b1e" }} />;
    default:
      return <></>;
  }
}

export default function ListItems() {
  return (
    <List component="nav" sx={{ mr: 10, mt: 2 }}>
      {listConfig?.map((item) => {
        return (
          <ListItemButton href={item?.path} key={item?.name}>
            <ListItemIcon sx={{ color: item?.color }}>
              {GetIcons(item?.name)}
            </ListItemIcon>
            <ListItemText
              primary={item?.name}
              sx={{ color: item?.color, display: { xs: "none", md: "flex" } }}
            />
          </ListItemButton>
        );
      })}
    </List>
  );
}

Alt text

DashBoard.js

  • Inside the DashBoard component import the layout which the parent component.
  • after that just use for loading message.
  • This component will be rendered only when don't render the result the url.
import Box from "@mui/system/Box";
import Layout from "./Layout";
import Typography from "@mui/material/Typography";
export default function DashBoard() {
  return (
    <Layout>
      <Box sx={{ mt: 5 }}>
        <Typography variant="h3" color="#000000">
          Coming soon...
        </Typography>
      </Box>
    </Layout>
  );
}

Layout.js

  • Take the children as the props.
  • This is the parent component of the DashBoard feature.
  • import the navbar of the dashboard and render it inside the fragment.
  • Implement the Grid System left and right.Left space keep it low as 2 for rendering the .In right space render the DashBoard.js which will get children
  • The {childrens} while be Cards and PieChart.
   <Grid container spacing={1}>
          <Grid
            item
            xs={2}
            sx={{
              mt: 2,
              backgroundColor: "#21005D",
              height: "100vh",
              justifyContent: "left",
            }}
          >
            <ListItems />
          </Grid>
          <Grid item xs={10} sx={{ mt: 4, color: "#FFFFFF" }}>
            {children}
          </Grid>
        </Grid>
  • export this component.
import React from "react";
import Box from "@mui/material/Box";
import ListItems from "./ListItems";
import DashBoardNavBar from "./Navbar";
import Grid from "@mui/material/Grid";

function Layout({ children }) {
  return (
    <>
      <DashBoardNavBar />
      <Box sx={{ display: { xs: "flex", md: "none" }, mt: 4 }}>
        <Grid container spacing={1}>
          <Grid
            item
            xs={2}
            sx={{
              mt: 2,
              backgroundColor: "#21005D",
              height: "100vh",
              justifyContent: "left",
            }}
          >
            <ListItems />
          </Grid>
          <Grid item xs={10} sx={{ mt: 4, color: "#FFFFFF" }}>
            {children}
          </Grid>
        </Grid>
      </Box>

// for the web responsivness

      <Box sx={{ display: { xs: "none", md: "flex" }, mt: 4 }}>
        <Grid container spacing={2}>
          <Grid
            item
            xs={2}
            sx={{
              mt: 2,
              backgroundColor: "#fffbff",
              height: "100vh",
            }}
          >
            <ListItems />
          </Grid>
          <Grid item xs={10} sx={{ mt: 10, color: "#FFFFFF" }}>
            {children}
          </Grid>
        </Grid>
      </Box>
    </>
  );
}

export default Layout;

Here We have completed the first part of the Dashboard page. Now towards displaying of the Accessibility details of the url.

ContextState.js

  • Now we will implement context method to create context.
  • create the context using AppContext.
  • create the custom context named as AppContextWrapper.
  • Apply useReducer() for management of the states.
import React, { createContext, useContext, useReducer } from "react";

export const AppContext = createContext({});

const AppContextWrapper = ({ children }) => {
  const reducer = (state, items) => {
    return { ...state, items: items };
  };

  const [state, dispatch] = useReducer(reducer, {});
  • use Local storage for storing the data because it helps to retrieve the data after refresh.
  • initialize a function by using React.useCallback hook passing items as an argument to set the values inside the local storage using the Json.stringify() method.
  const addItem = React.useCallback((item) => {
    localStorage.setItem("sections", JSON.stringify(item));
    dispatch(item);
  }, []);
  • Initialize a function by using React.useCallback hook passing to remove the value from the local storage.
  • Initialize the function for getToken();
  const removeItem = React.useCallback(() => {
    localStorage.setItem("sections", JSON.stringify([]));
    dispatch([]);
  }, []);
  • Store the token in function variable. use cookie method to retrain the token
  • Initialize this using React.useCallback(()=>{})
  const Authenticate = React.useCallback((token) => {
    document.cookie = `token=${token}; SameSite=None; Secure`;
  }, []);
  • Initialize the function by using React.useCallback hook for authentication purpose as the token as parameter. call document.cookie=``.
  • here we receive the token in the formate token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImJhbGFqaS5oYW1iZXJlQGdtYWlsLmNvbSIsInBhc3N3b3JkIjoic2FuZHkiLCJpYXQiOjE2ODMyNjk2NzAsImV4cCI6MTY4MzMxMjg3MH0.beYq1k7sPX2GdryNQ0MG2Z5f2-Zkpfp7butM152A5cE; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImJhbGFqaS5oYW1iZXJlQGdtYWlsLmNvbSIsInBhc3N3b3JkIjoic2FuZHkiLCJpYXQiOjE2ODMyNjc0MDksImV4cCI6MTY4MzMxMDYwOX0.eXbdUjd8btuDyTvt0rSSGMXamrvDd18VMaldbjOVUNs
  • We need to modify this token
  const getToken = () => {
    return document.cookie
      .split("; ")
      .find((row) => row.startsWith("token="))
      ?.split("=")[1];
  };
  const logOut = React.useCallback(() => {
    document.cookie = `token=${""}; SameSite=None; Secure`;
  }, []);
  • Initialize the function for logOut by using React.useCallBack();
  • Now just create the function to check whether it is isAuthenticated.
  • In this we will be token by parsing it into required data.
  const isAuthenticated = () => {
    const cookieValue = document.cookie
      .split("; ")
      .find((row) => row.startsWith("token="))
      ?.split("=")[1];
    if (cookieValue) {
      return true;
    }
    return false;
  };
  • Now by the help of useEffect load the data which is saved in local storage data.
  • We will dispatch the data to the useReducer state.
  React.useEffect(() => {
    if (localStorage.getItem("sections")) {
      dispatch(JSON.parse(localStorage.getItem("sections")));

  }, []);
}
  • By using <AppContext.provider> provide the methods inside the value={{}} which where declared <AppContext.provider>.
  • export AppContextWrapper, useAppContext.
  return (
    <AppContext.Provider
      value={{
        ...state,
        addItem,
        removeItem,
        Authenticate,
        isAuthenticated,
        logOut,
        getToken,
      }}
    >
      {children}
    </AppContext.Provider>
  );
};
  • Here we converted it into custom hook.
  • we configure this context in parent component in App.js
const useAppContext = () => {
  return useContext(AppContext);
};
export { AppContextWrapper, useAppContext };

Code AllTogether

import React, { createContext, useContext, useReducer } from "react";

export const AppContext = createContext({});

const AppContextWrapper = ({ children }) => {
  const reducer = (state, items) => {
    return { ...state, items: items };
  };

  const [state, dispatch] = useReducer(reducer, {});
  // console.log("states", state);
  const addItem = React.useCallback((item) => {
    console.log("callback", item);
    localStorage.setItem("sections", JSON.stringify(item));
    dispatch(item);
  }, []);

  const removeItem = React.useCallback(() => {
    localStorage.setItem("sections", JSON.stringify([]));
    dispatch([]);
  }, []);

  const Authenticate = React.useCallback((token) => {
    document.cookie = `token=${token}; SameSite=None; Secure`;
  }, []);

  const getToken = () => {
    console.log("cookie", document.cookie);
    return document.cookie
      .split("; ")
      .find((row) => row.startsWith("token="))
      ?.split("=")[1];
  };

  const logOut = React.useCallback(() => {
    document.cookie = `token=${""}; SameSite=None; Secure`;
  }, []);

  const isAuthenticated = () => {
    const cookieValue = document.cookie
      .split("; ")
      .find((row) => row.startsWith("token="))
      ?.split("=")[1];
    if (cookieValue) {
      return true;
    }
    return false;
  };

  React.useEffect(() => {
    if (localStorage.getItem("sections")) {
      dispatch(JSON.parse(localStorage.getItem("sections")));
    }
  }, []);

  return (
    <AppContext.Provider
      value={{
        ...state,
        addItem,
        removeItem,
        Authenticate,
        isAuthenticated,
        logOut,
        getToken,
      }}
    >
      {children}
    </AppContext.Provider>
  );
};

const useAppContext = () => {
  return useContext(AppContext);
};

export { AppContextWrapper, useAppContext };

Scan

Chart.js

  • import the Chart from the chart.js and Pie chart from react-chartjs-2.
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
import { Pie } from "react-chartjs-2";
  • Create manual color configuration for the pieChart purpose.
ChartJS.register(ArcElement, Tooltip, Legend);

function chartConfig(arr) {
  return {
    labels: ["Violations", "In Applicable", "In Complete", "Passes"],
    datasets: [
      {
        // label: "# of Votes",
        data: arr,
        backgroundColor: ["#8C1D18", "#938F99", "#FFD8E4", "#D0BCFF"],
        borderColor: ["#8C1D18  ", "#938F99", "#FFD8E4", "#D0BCFF"],
        borderWidth: 1,
      },
    ],
  };
}
  • Render the results which are obtained from the ScanDetails component.In the pieChart MUI component.
export default function PieChart(props) {
  const { results } = props;
  const arr = [
    results?.violations?.length,
    results?.inapplicable?.length,
    results?.incomplete?.length,
    results?.passes?.length,
  ];
  return (
    <Pie
      data={chartConfig(arr)}
      width={"90%"}
      height={"350px"}
      options={{ maintainAspectRatio: false }}
    />
  );
}
  • Get the Pie chart sample code from React Mui.
  • export this component Alt text

Code AllTogether

import React from "react";
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
import { Pie } from "react-chartjs-2";

ChartJS.register(ArcElement, Tooltip, Legend);

function chartConfig(arr) {
  return {
    labels: ["Violations", "In Applicable", "In Complete", "Passes"],
    datasets: [
      {
        // label: "# of Votes",
        data: arr,
        backgroundColor: ["#8C1D18", "#938F99", "#FFD8E4", "#D0BCFF"],
        borderColor: ["#8C1D18  ", "#938F99", "#FFD8E4", "#D0BCFF"],
        borderWidth: 1,
      },
    ],
  };
}

export default function PieChart(props) {
  const { results } = props;
  const arr = [
    results?.violations?.length,
    results?.inapplicable?.length,
    results?.incomplete?.length,
    results?.passes?.length,
  ];
  return (
    <Pie
      data={chartConfig(arr)}
      width={"90%"}
      height={"350px"}
      options={{ maintainAspectRatio: false }}
    />
  );
}

ScanDetails.js

  • Here we take the data from the custom context which was created above.
import React, { useState, useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom";
import Grid from "@mui/material/Grid";
import Layout from "../DashBoard/Layout";
import { Card, Typography } from "@mui/material";
import PieChart from "./Chart";
import { useAppContext } from "../ContextState";
import Link from "@mui/material/Link";
  • Initialize the array of object with name and background color.
const pairs = [
  {
    name: "passes",
    backgroundColor: "#D0BCFF",
  },
  {
    name: "inapplicable",

    backgroundColor: "#938F99",
  },
  {
    name: "incomplete",
    backgroundColor: "#FFD8E4",
  },
  {
    name: "violations",
    backgroundColor: "#8C1D18",
  },
];
  • Extract the functions from context api.
  • get the id of current scan element using useParams() method of react-router-dom.
  • navigate to the /scans/:id/details page.
  • rows state get array of objects with accessibility results.
export default function ScanDetails() {
  let { id } = useParams();
  const [rows, setRows] = useState();
  const navigate = useNavigate();
  const scanDetails = rows && rows?.ScanDetails[0];
  const { addItem, removeItem } = useAppContext();
  • fetch the issue through the api using the id from the params.
  • useEffect() hook is used to display the scan details.
  useEffect(() => {
    fetch(`http://localhost:8000/api/scans/${id}`)
      .then((response) => response.json())
      .then((res) => setRows(res?.data));
  }, [id]);
  • conditionally display the issue separately in the cards.
  • after getting selected navigate to the scan detail page.
  const handleAddItem = (name) => {
    removeItem();
    switch (name) {
      case "passes":
        addItem(scanDetails?.results?.passes);
        break;
      case "inapplicable":
        addItem(scanDetails?.results?.inapplicable);
        break;
      case "incomplete":
        addItem(scanDetails?.results?.incomplete);
        break;
      case "violations":
        addItem(scanDetails?.results?.violations);
        break;
      default:
        addItem([]);
    }
    navigate(`/scans/${id}/${name?.toLowerCase()}`);
  };
  • Display the length of the issue in the cards by using switch method.
  const chooseResultsCount = (name) => {
    switch (name) {
      case "passes":
        return scanDetails?.results?.passes?.length;
      case "inapplicable":
        return scanDetails?.results?.inapplicable?.length;
      case "incomplete":
        return scanDetails?.results?.incomplete?.length;
      case "violations":
        return scanDetails?.results?.violations?.length;
      default:
        return 0;
    }
  };
  • Handler function for adding the item.
  const handleViewDetailsClick = (e, name) => {
    e.preventDefault();
    handleAddItem(name);
  };
  • Displaying the issues in the cards.
  return (
    <Layout>
      <Grid container spacing={2} sx={{ display: { xs: "none", md: "flex" } }}>
        <Grid item xs={6}>
          <Grid container spacing={2}>
            {pairs?.map((item) => {
              return (
                <Grid item xs={6} key={item.name}>
                  <Card
                    sx={{
                      height: "110px",
                      background: item?.backgroundColor,
                      p: 2,
                    }}
                  >
                    <Typography variant="h6">{item?.name}</Typography>
                    <Typography variant="h3" sx={{ textAlign: "center" }}>
                      {chooseResultsCount(item?.name)}
                    </Typography>
                    <Typography sx={{ textAlign: "right" }}>
                      <Link
                        onClick={(e) => handleViewDetailsClick(e, item?.name)}
                        sx={{ color: "black" }}
                      >
                        View Details{" "}
                      </Link>
                    </Typography>
                  </Card>
                </Grid>
              );
            })}
          </Grid>
        </Grid>
        <Grid item xs={6}>
          <PieChart results={scanDetails?.results} sx={{ height: "50px" }} />
        </Grid>
      </Grid>

      {/* MobileLayout */}
      <Grid
        container
        spacing={2}
        sx={{ display: { xs: "flex", md: "none" }, mt: 2 }}
      >
        <Grid item xs={12}>
          <Grid container spacing={2}>
            {pairs?.map((item) => {
              return (
                <Grid item xs={6} key={item.name}>
                  <Card
                    sx={{
                      p: 1,
                      flexDirection: "column",
                      background: item?.backgroundColor,
                    }}
                  >
                    <Typography variant="h6">{item?.name}</Typography>
                    <Typography variant="h3" sx={{ textAlign: "center" }}>
                      {chooseResultsCount(item?.name)}
                    </Typography>
                    <Typography sx={{ textAlign: "right" }}>
                      <Link
                        onClick={(e) => handleViewDetailsClick(e, item?.name)}
                        sx={{ color: "black" }}
                      >
                        View Details{" "}
                      </Link>
                    </Typography>
                  </Card>
                </Grid>
              );
            })}
          </Grid>
        </Grid>
        <Grid item xs={12} sx={{ mt: 2 }}>
          <PieChart results={scanDetails?.results} />
        </Grid>
      </Grid>
    </Layout>
  );
}

Alt text

Code AllTogether

import React, { useState, useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom";
import Grid from "@mui/material/Grid";
import Layout from "../DashBoard/Layout";
import { Card, Typography } from "@mui/material";
import PieChart from "./Chart";
import { useAppContext } from "../ContextState";
import Link from "@mui/material/Link";

const pairs = [
  {
    name: "passes",
    backgroundColor: "#D0BCFF",
  },
  {
    name: "inapplicable",

    backgroundColor: "#938F99",
  },
  {
    name: "incomplete",
    backgroundColor: "#FFD8E4",
  },
  {
    name: "violations",
    backgroundColor: "#8C1D18",
  },
];

export default function ScanDetails() {
  let { id } = useParams();
  const [rows, setRows] = useState();
  const navigate = useNavigate();
  const scanDetails = rows && rows?.ScanDetails[0];
  const { addItem, removeItem } = useAppContext();

  useEffect(() => {
    fetch(`http://localhost:8000/api/scans/${id}`)
      .then((response) => response.json())
      .then((res) => setRows(res?.data));
  }, [id]);

  const handleAddItem = (name) => {
    removeItem();
    switch (name) {
      case "passes":
        addItem(scanDetails?.results?.passes);
        break;
      case "inapplicable":
        addItem(scanDetails?.results?.inapplicable);
        break;
      case "incomplete":
        addItem(scanDetails?.results?.incomplete);
        break;
      case "violations":
        addItem(scanDetails?.results?.violations);
        break;
      default:
        addItem([]);
    }
    navigate(`/scans/${id}/${name?.toLowerCase()}`);
  };

  const chooseResultsCount = (name) => {
    switch (name) {
      case "passes":
        return scanDetails?.results?.passes?.length;
      case "inapplicable":
        return scanDetails?.results?.inapplicable?.length;
      case "incomplete":
        return scanDetails?.results?.incomplete?.length;
      case "violations":
        return scanDetails?.results?.violations?.length;
      default:
        return 0;
    }
  };
  const handleViewDetailsClick = (e, name) => {
    e.preventDefault();
    handleAddItem(name);
  };

  return (
    <Layout>
      <Grid
        container
        spacing={2}
        sx={{ display: { xs: "none", md: "flex" } }}
      >
        <Grid item xs={6} >
          <Grid container spacing={2} >
            {pairs?.map((item) => {
              return (
                <Grid item xs={6} key={item.name} >
                  <Card
                    sx={{
                      height: "110px",
                      background: item?.backgroundColor,
                      p: 2,
                    }}
                  >
                    <Typography variant="h6">{item?.name}</Typography>
                    <Typography variant="h3" sx={{ textAlign: "center" }}>
                      {chooseResultsCount(item?.name)}
                    </Typography>
                    <Typography sx={{ textAlign: "right" }}>
                      <Link
                        // onClick={(e) => handleViewDetailsClick(e, item?.name)}
                        onClick={(e) => handleAddItem(item?.name)}
                        sx={{ color: "black" }}
                      >
                        View Details{" "}
                      </Link>
                    </Typography>
                  </Card>
                </Grid>
              );
            })}
          </Grid>
        </Grid>
        <Grid item xs={6}>
          <PieChart results={scanDetails?.results} sx={{ height: "50px" }} />
        </Grid>
      </Grid>






      {/* MobileLayout */}
      <Grid
        container
        spacing={2}
        sx={{ display: { xs: "flex", md: "none" }, mt: 2 }}
      >
        <Grid item xs={12}>
          <Grid container spacing={2}>
            {pairs?.map((item) => {
              return (
                <Grid item xs={6} key={item.name}>
                  <Card
                    sx={{
                      p: 1,
                      flexDirection: "column",
                      background: item?.backgroundColor,
                    }}
                  >
                    <Typography variant="h6">{item?.name}</Typography>
                    <Typography variant="h3" sx={{ textAlign: "center" }}>
                      {chooseResultsCount(item?.name)}
                    </Typography>
                    <Typography sx={{ textAlign: "right" }}>
                      <Link
                        onClick={(e) => handleViewDetailsClick(e, item?.name)}
                        sx={{ color: "black" }}
                      >
                        View Details{" "}
                      </Link>
                    </Typography>
                  </Card>
                </Grid>
              );
            })}
          </Grid>
        </Grid>
        <Grid item xs={12} sx={{ mt: 2 }}>
          <PieChart results={scanDetails?.results} />
        </Grid>
      </Grid>
    </Layout>
  );
}

Scans.js

  • import this components.
import React, { useState, useEffect, useCallback } from "react";
import Layout from "../DashBoard/Layout";
import Link from "@mui/material/Link";
import Container from "@mui/system/Container";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import { dateFormat } from "../utils/helper";
import { DataGrid } from "@mui/x-data-grid";
import { useNavigate } from "react-router-dom";
import FormController from "../Components/Forms/FormController";
import { validUrl } from "../utils/helper";
import { useAppContext } from "../ContextState";
  • Create the array of the object with entities field headerName flex sortable.
  • valueGetter() for custom logic. (A Value Getter is a function that gets called allowing values to be pulled from literally anywhere, including executing any expressions you wish along the way)
const columns = [
  { field: "url", headerName: "URL", flex: 1, sortable: false },
  {
    field: "createdAt",
    headerName: "CREATED DATE",
    flex: 1,
    valueGetter: (params) => dateFormat(params?.row?.createdAt),
  },
  {
    field: "results",
    headerName: "RESULTS",
    flex: 1,
    sortable: false,
    renderCell: (params) => (
      <Link href={`/scans/${params?.row?.id}`}>View Details</Link>
    ),
  },
];
  • Get method getToken from context.
  • Create the states for searchValue.
  • Initiate Pagination state and sorting state
export default function ScanTable() {
  const { getToken } = useAppContext();
  const [selectionModel, setSelectionModel] = useState();
  const navigate = useNavigate();
  const [data, setData] = useState();
  const [searchValue, setSearchValue] = useState("");
  const [paginationModel, setPaginationModel] = React.useState({
    pageSize: 5,
    page: 0,
  });

  const [sortingModel, setSortingModel] = React.useState({
    field: "createdAt",
    sort: "asc",
  });
  • Post the sorting implementation to the api so that it can load in the pagination manner.
  • If the user want to access this records they need to be signined that is authorized.
  • So we get pass the getToken() to the headers for authentication.
  • check the condition whether valid user or not then navigate it.
  useEffect(() => {
    fetch(`http://localhost:8000/api/scans/`, {
      method: "POST",
      body: JSON.stringify({
        page: paginationModel.page,
        pageSize: paginationModel.pageSize,
        search: searchValue,
        field: sortingModel.field,
        sort: sortingModel.sort,
      }),
      headers: {
        "Content-type": "application/json; charset=UTF-8",
        authorization: `Bearer ${getToken()}`,
      },
    })
      .then((response) => response.json())
      .then((data) => {
        if (data.code === "401") {
          navigate(`/user/signin`);
        }
        setData(data);
      });
  }, [
    paginationModel.page,
    paginationModel.pageSize,
    sortingModel.field,
    sortingModel.sort,
    searchValue,
    navigate,
  ]);
  const handleCallback = useCallback(() => {
    fetch("http://localhost:8000/api/scans", {
      method: "POST",
      body: JSON.stringify({
        page: paginationModel.page,
        pageSize: paginationModel.pageSize,
        search: searchValue,
        order: ["createdAt", "ASC"],
        field: sortingModel.field,
        sort: sortingModel.sort,
      }),
      headers: {
        "Content-type": "application/json; charset=UTF-8",
      },
    })
      .then((response) => response.json())
      .then((data) => setData(data));
  }, [
    paginationModel.page,
    paginationModel.pageSize,
    searchValue,
    sortingModel.field,
    sortingModel.sort,
  ]);
  • Used DataGrid for implementing pagination and sorting of the data
  return (
    <Layout>
      <Container>
        <Box
          sx={{
            display: { xs: "none", md: "flex" },
            flexDirection: "row",
            justifyContent: "center",
            mt: 2,
          }}
        >
          <FormController
            setUrl={setSearchValue}
            url={searchValue}
            validUrl={validUrl}
            handleCallback={handleCallback}
            width="100%"
            buttonName="Search"
          />
        </Box>
        {/* {console.log("data", data)} */}
        {data ? (
          <div style={{ height: 400, width: "100%" }}>
            <DataGrid
              sx={{
                mt: 2,
                // height: "0px",
              }}
              disableColumnFilter
              disableColumnMenu
              disableColumnSelector
              rows={data?.rows}
              rowCount={data?.count}
              columns={columns}
              paginationModel={paginationModel}
              onPaginationModelChange={setPaginationModel}
              rowsPerPageOptions={[5]}
              paginationMode="server"
              onSelectionModelChange={(selection) => {
                if (selection.length > 1) {
                  const selectionSet = new Set(selectionModel);
                  const result = selection.filter((s) => !selectionSet.has(s));

                  setSelectionModel(result);
                } else {
                  setSelectionModel(selection);
                }
              }}
              checkboxSelection
              sortingMode="server"
              sortingModel={sortingModel}
              onSortModelChange={(model) => setSortingModel(model[0])}
            />
          </div>
        ) : (
          <Box>
            <Typography
              component="h1"
              variant="h4"
              color="#1d1b1e"
              sx={{ mt: 2 }}
            >
              Records not found
            </Typography>
          </Box>
        )}
      </Container>
    </Layout>
  );
}

Code AllTogether

import React, { useState, useEffect, useCallback } from "react";
import Layout from "../DashBoard/Layout";
import Link from "@mui/material/Link";
import Container from "@mui/system/Container";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import { dateFormat } from "../utils/helper";
import { DataGrid } from "@mui/x-data-grid";
import { useNavigate } from "react-router-dom";
import FormController from "../Components/Forms/FormController";
import { validUrl } from "../utils/helper";
import { useAppContext } from "../ContextState";
import { filterItems } from "../utils/helper";

const columns = [
  { field: "url", headerName: "URL", flex: 1, sortable: false },
  {
    field: "createdAt",
    headerName: "CREATED DATE",
    flex: 1,
    valueGetter: (params) => dateFormat(params?.row?.createdAt),
  },
  {
    field: "results",
    headerName: "RESULTS",
    flex: 1,
    sortable: false,
    renderCell: (params) => (
      <Link href={`/scans/${params?.row?.id}`}>View Details</Link>
    ),
  },
];
export default function ScanTable() {
  const { getToken } = useAppContext();
  const [selectionModel, setSelectionModel] = useState();
  const navigate = useNavigate();
  const [data, setData] = useState();
  const [searchValue, setSearchValue] = useState("");
  const [paginationModel, setPaginationModel] = React.useState({
    pageSize: 5,
    page: 0,
  });

  const [sortingModel, setSortingModel] = React.useState({
    field: "createdAt",
    sort: "asc",
  });

  useEffect(() => {
    fetch(`http://localhost:8000/api/scans/`, {
      method: "POST",
      body: JSON.stringify({
        page: paginationModel.page,
        pageSize: paginationModel.pageSize,
        search: searchValue,
        field: sortingModel.field,
        sort: sortingModel.sort,
      }),
      headers: {
        "Content-type": "application/json; charset=UTF-8",
        authorization: `Bearer ${getToken()}`,
      },
    })
      .then((response) => response.json())
      .then((data) => {
        if (data.code === "401") {
          navigate(`/user/signin`);
        }
        setData(data);
      });
  }, [
    paginationModel.page,
    paginationModel.pageSize,
    sortingModel.field,
    sortingModel.sort,
    searchValue,
    navigate,
    getToken,
  ]);

  const handleCallback = useCallback(() => {
    fetch("http://localhost:8000/api/scans", {
      method: "POST",
      body: JSON.stringify({
        page: paginationModel.page,
        pageSize: paginationModel.pageSize,
        search: searchValue,
        order: ["createdAt", "ASC"],
        field: sortingModel.field,
        sort: sortingModel.sort,
      }),
      headers: {
        "Content-type": "application/json; charset=UTF-8",
      },
    })
      .then((response) => response.json())
      .then((data) => setData(data));
  }, [
    paginationModel.page,
    paginationModel.pageSize,
    searchValue,
    sortingModel.field,
    sortingModel.sort,
  ]);

  // trying to add filter search

  function filterItems(items, searchValue) {
    searchValue = searchValue.toLowerCase();
     var item= items.filter((item) =>
      item.url.split(" ").some((word) => word.toLowerCase().includes(searchValue))

    );
    console.log('items',item)
    setData(item)
  }
  return (
    <Layout>
      <Container>
        <Box
          sx={{
            display: { xs: "none", md: "flex" },
            flexDirection: "row",
            justifyContent: "center",
            mt: 2,
          }}
        >
          <FormController
            setUrl={setSearchValue}
            url={searchValue}
            validUrl={validUrl}
            // onChange={()=>filterItems(data,searchValue)}
            handleCallback={handleCallback}
            width="100%"
            buttonName="Search"
          />
        </Box>
        {/* {console.log("data", data)} */}
        {data ? (
          <div style={{ height: 400, width: "100%" }}>
            <DataGrid
              sx={{
                mt: 2,
                // height: "0px",
              }}
              disableColumnFilter
              disableColumnMenu
              disableColumnSelector
              rows={data?.rows}
              rowCount={data?.count}
              columns={columns}
              paginationModel={paginationModel}
              onPaginationModelChange={setPaginationModel}
              rowsPerPageOptions={[5]}
              paginationMode="server"
              onSelectionModelChange={(selection) => {
                if (selection.length > 1) {
                  const selectionSet = new Set(selectionModel);
                  const result = selection.filter((s) => !selectionSet.has(s));

                  setSelectionModel(result);
                } else {
                  setSelectionModel(selection);
                }
              }}
              checkboxSelection
              sortingMode="server"
              sortingModel={sortingModel}
              onSortModelChange={(model) => setSortingModel(model[0])}
            />
          </div>
        ) : (
          <Box>
            <Typography
              component="h1"
              variant="h4"
              color="#1d1b1e"
              sx={{ mt: 2 }}
            >
              Records not found
            </Typography>
          </Box>
        )}
      </Container>
    </Layout>
  );
}

IssueDetails.js

  • import all the component which are given below.

  • extract item from the useAppContext.

  • map the item and show it in the Accordion

  • Get the items from context.

  const { items } = useAppContext();
  • Give the parent component as

Code AllTogether

import React from "react";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import Typography from "@mui/material/Typography";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { useAppContext } from "../ContextState";
import Layout from "../DashBoard/Layout";
import Grid from "@mui/material/Grid";


export default function IssueDetails() {
  const { items } = useAppContext();

  return (
    <Layout>
      <Grid container spacing={2}>
        {items?.map((item) => {
          return (
            <Grid item xs={12} md={6} key={item?.id}>
              <Accordion>
                <AccordionSummary
                  expandIcon={<ExpandMoreIcon />}
                  aria-controls="panel1a-content"
                  id="panel1a-header"
                >
                  <Typography>{item?.id}</Typography>
                </AccordionSummary>
                <AccordionDetails sx={{ ml: 0 }}>
                  <Typography>
                    <strong>Description:</strong> {item?.description}.
                  </Typography>
                  <Typography>
                    <strong>Help:</strong> {item?.help}.
                  </Typography>
                  <Typography>
                    <strong>HelpUrl:</strong>{" "}
                    <a href={item?.helpUrl} target="_blank" rel="noreferrer">
                      Click Here
                    </a>
                  </Typography>
                  <Typography>
                    <strong>Impact:</strong> {item?.impact}.
                  </Typography>
                </AccordionDetails>
              </Accordion>

            </Grid>
          );
        })}
      </Grid>
    </Layout>
  );
}

Utils

helper.js

  • This are the reusable component

  • dateFormatter function which formats the date.

  • validUrl() which checks whether the given url is valid in the input box.which is used in the TestUrl home feature component.

  • for the DateFormat take the date as parameter

  • create an array of months.Extract the date from the Date constructor.

  const newDate = new Date(date);
  const monthNames = [
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec",
  ];
  • Now by using instance method getDate(),getMonth() get monthIndex and day.
  • Pass this monthIndex value to array and get month.
  • get the year by passing getFullYear() instance method.
  • return all this through string literals.
 const day = newDate.getDate();
  const monthIndex = newDate.getMonth();
  const monthName = monthNames[monthIndex];
  const year = newDate.getFullYear();
  return `${day} ${monthName} ${year}`;
  • for validation of the input url pass the parameter url in try/catch block in new URL().
export  function validUrl(inputUrl){
  try {
    new URL(inputUrl);
    return true;
  } catch (error) {
    return false;
  }
};

Code AllTogether

export function dateFormat(date) {
  const newDate = new Date(date);
  const monthNames = [
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec",
  ];
  const day = newDate.getDate();
  const monthIndex = newDate.getMonth();
  const monthName = monthNames[monthIndex];
  const year = newDate.getFullYear();
  return `${day} ${monthName} ${year}`;
}

export  function validUrl(inputUrl){
  try {
    new URL(inputUrl);
    return true;
  } catch (error) {
    return false;
  }
};

Components

Copyright

  • This the copy right footer which is made common so that we can reuse it.
import * as React from "react";
import Link from "@mui/material/Link";
import Typography from "@mui/material/Typography";

export default function Copyright(props) {
  return (
    <Typography
      variant="body2"
      color="text.secondary"
      align="center"
      {...props}
    >
      {"Copyright © "}
      <Link color="inherit" href="https://mui.com/">
        Webatool
      </Link>{" "}
      {new Date().getFullYear()}
      {"."}
    </Typography>
  );
}
  • This the form component which is made reusable component so that we can use it where we want in our application.
  • This component is used in home.js and scansDetails.js
import React from "react";
import Button from "@mui/material/Button";
import OutlinedInput from "@mui/material/OutlinedInput";
import FormControl from "@mui/material/FormControl";
import Box from "@mui/material/Box/Box";

function FormController({ url, setUrl, validUrl, handleCallback, buttonName }) {
  return (
    <Box>
      {" "}
      <FormControl
        variant="outlined"
        width="100%"
        fullWidth
        sx={{ width: "650px", mt: 1, ml: 6 }}
      >
        <OutlinedInput
          sx={{
            color: "#49454e",
            backgroundColor: "#e7e0eb",
            border: "none",
            "& fieldset": { border: "none" },
            "&:focus": {
              backgroundColor: "#cbc4cf",
            },
            "&:hover": {
              backgroundColor: "#cbc4cf",
            },
          }}
          role="textbox"
          placeholder="place url here"
          aria-placeholder="place url here"
          size="large"
          onChange={(e) => setUrl(e.target.value)}
          id="url-text"
        />
      </FormControl>
      <Button
        tabIndex={0}
        aria-pressed="false"
        role="button"
        variant="contained"
        sx={{
          backgroundColor: "#7331df",
          textTransform: "none",
          color: "#ffffff",
          ml: 2,
          mt: 1,
          "&:focus": {
            backgroundColor: "#7331df",
          },
          "&:hover": {
            backgroundColor: "#7331df",
          },
          "&:visited": {
            backgroundColor: "#7331df",
          },
          height:'55px'
        }}
        onClick={handleCallback}
        disabled={!validUrl(url)}
      >
        {buttonName}
      </Button>
    </Box>
  );
}

export default FormController;

Auth

  • By using the conditional rendering we have created both signUp and sigIn page in single component.

Signup.js

import * as React from "react";
import Avatar from "@mui/material/Avatar";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel";
import Checkbox from "@mui/material/Checkbox";
import Link from "@mui/material/Link";
import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
import Typography from "@mui/material/Typography";
import Copyright from "../Components/Copyright";
import Container from "@mui/material/Container";
import { useLocation } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { useAppContext } from "../ContextState";
function validation(input, fieldName, target) {
  for (const property in input.validity) {
    const type = input.validity[property] ? property : "";

    console.log(type, fieldName);
    switch (type) {
      case "valueMissing":
        return `${fieldName} is required`;
      case "patternMismatch":
        return `${fieldName} is not valid`;
      default:
        return "";
    }
  }
}
export default function SignUp() {
  const { Authenticate, isAuthenticated, getToken } = useAppContext();
  const [formState, setFormState] = React.useState(new Map());

  const { pathname } = useLocation();
  const navigate = useNavigate();

  const [checked, setChecked] = React.useState(false);

  const handleChange = (event) => {
    setChecked(event.target.checked);
  };

  // console.log("formState", formState);

  const handleSubmit = (event) => {
    event.preventDefault();

    const data = new FormData(event.currentTarget);
    fetch(
      `http://localhost:8000/api/user/${
        pathname.includes("signup") ? "signup" : "signin"
      }`,
      {
        method: "POST",
        body: JSON.stringify({
          email: data.get("email"),
          password: data.get("password"),
          firstName: data.get("firstName"),
          lastName: data.get("lastName"),
          allowExtraEmails: checked,
        }),
        headers: {
          "Content-type": "application/json; charset=UTF-8",
        },
      }
    )
      .then((response) => response.json())
      .then((data) => {
        if (data?.success && data?.data?.token) {
          Authenticate(data?.data?.token);
          console.log(isAuthenticated());
          if (isAuthenticated()) {
            navigate(`/scans`);
          }
        } else if (data?.success) {
          navigate(`/user/signin`);
        }
      });
  };

  return (
    <Container component="main" maxWidth="xs">
      <Box
        sx={{
          marginTop: 8,
          display: "flex",
          flexDirection: "column",
          alignItems: "center",
        }}
      >
        <Avatar sx={{ m: 1, bgcolor: "secondary.main" }}>
          <LockOutlinedIcon />
        </Avatar>
        <Typography component="h1" variant="h5">
          {`Sign ${pathname.includes("signup") ? "up" : "in"}`}
        </Typography>
        <Box component="form" noValidate onSubmit={handleSubmit} sx={{ mt: 3 }}>
          <Grid container spacing={2}>
            {pathname.includes("signup") ? (
              <>
                <Grid item xs={12} sm={6}>
                  <TextField
                    error={formState.get("firstName") ? true : false}
                    autoComplete="given-name"
                    name="firstName"
                    required
                    fullWidth
                    id="firstName"
                    label="First Name"
                    helperText={
                      formState.get("firstName")
                        ? formState.get("firstName")
                        : undefined
                    }
                  />
                </Grid>
                <Grid item xs={12} sm={6}>
                  <TextField
                    error={formState.get("lastName") ? true : false}
                    required
                    fullWidth
                    id="lastName"
                    label="Last Name"
                    name="lastName"
                    autoComplete="family-name"
                    helperText={
                      formState.get("lastName")
                        ? formState.get("lastName")
                        : undefined
                    }
                  />
                </Grid>
              </>
            ) : undefined}

            <Grid item xs={12}>
              <TextField
                inputProps={{
                  inputMode: "email",
                  pattern: "^[a-zA-Z0-9]+@(?:[a-zA-Z0-9]+.)+[A-Za-z]+$",
                }}
                error={formState.get("email") ? true : false}
                required
                fullWidth
                id="email"
                label="Email Address"
                name="email"
                type="email"
                autoComplete="email"
                helperText={
                  formState.get("email") ? formState.get("email") : undefined
                }
              />
            </Grid>
            <Grid item xs={12}>
              <TextField
                error={formState.get("password") ? true : false}
                required
                fullWidth
                name="password"
                label="Password"
                type="password"
                id="password"
                autoComplete="new-password"
                helperText={
                  formState.get("password")
                    ? formState.get("password")
                    : undefined
                }
              />
            </Grid>
            {pathname.includes("signup") ? (
              <Grid item xs={12}>
                <FormControlLabel
                  control={
                    <Checkbox
                      value="allowExtraEmails"
                      checked={checked}
                      onChange={handleChange}
                      color="primary"
                    />
                  }
                  label="I want to receive product updates via email."
                />
              </Grid>
            ) : undefined}
          </Grid>
          <Button
            type="submit"
            fullWidth
            variant="contained"
            sx={{ mt: 3, mb: 2 }}
          >
            {`Sign ${pathname.includes("signup") ? "up" : "in"}`}
          </Button>
          <Grid container justifyContent="flex-end">
            <Grid item>
              <Link
                href={
                  pathname.includes("signup") ? "/user/signin" : "/user/signup"
                }
                variant="body2"
              >
                {pathname.includes("signup")
                  ? "Already have an account? Sign in"
                  : "Create an Account"}
              </Link>
            </Grid>
          </Grid>
        </Box>
      </Box>
      <Copyright />
    </Container>
  );
}
⚠️ **GitHub.com Fallback** ⚠️