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
Folder Structure of react application.
Auth
SignUp.js
Component
Copyright
FormController
DashBoard
Dashboard
Layout.js
ListItems.js
Navbar.js
Home.js
AppInfo.js
Header.js
Home.js
Layout.js
Main.js
TestUrl.js
Scan
Chart.js
IssueDetails.js
newScans.js
ScanDetails.js
Scans.js
Utils
helper.js
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 is for navbar section of our home page.
- Initialize the array of object with two properties i.e name and link.
- Name which displays on the Navbar and link to navigate.
- Here we have created three component in this component.This components are HeaderMobile,HeaderWeb() and Header().
- Code for the HeaderMobile and HeaderWeb() are same, two components are there because for the responsiveness.
- HeaderMobile is for mobile page in this we use menu tab. For this initialize the state with useState with the state named as anchorElNav.
- Initialize two handler function handleOpenNavMenu for opening the menu bar and handleCloseNavMenu for closing the menu bar.
- handleOpenNavMenu map the Initialized array into of the navbar item such as Docs,DashBoard,login button will be mapped inside the Menu tab button.
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
- In this component take two flied one for Mobile responsiveness and another for web responsiveness.
- In starting box tag implement sx={{ display: { xs: "flex", md: "none" }}} content inside this box will be displayed in mobile screens.
- In another box tag implement sx={{ display: { xs: "none", md: "flex" }}} content inside this box will be displayed in larger screen.
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>
<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
- Basically, what we are doing in this component is we are rendering the input box and button in input box url in send to analyzing the web accessibility.
- import the useState and useCallback from the react
- import the useNavigate from react-router-dom because after the click of the we need to navigate to the dashboard.
- import CircularProgress from "@mui/material/CircularProgress" for showing the circular process after onClick.
- create two states url and loading using the useState..
- create the function using useCallBack() setLoading(true) and use fetch method to fetch the api and use method:"POST" for posting the url by stringyfying the url.
- after posting fetch data from the api set that into the setUrl(data) use if/else condition if data is not present throw an error. if data is present then navigate to the dashboard.
- set the condition for the CircularProgress to display. The condition is is the loading state is true then load the CircularProgress.
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
- We use here grid layer for partitioning the two components in two section using grid system.
- In one section inside the print the visual Analytics Reports.
- In another section import the image using
tag note load the image lazyly by using loading="lazy" inside the
.
- export this component.
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
- import all the previous components of the Home features inside this component.
- NOw arrange the component as per the requirement.
- Don't import the Header section for now we will deal that with later.
- set the state using useState name the state as error. This error will be used for the section we will pass this setState function as props to the testurl component.
- create two boxes one inside one, On inside box set the display as flex and flexDirection as column.
- load the component as the order , and .
- now it's time for showing for the popup message if the input box is field with in invalid url or if it is empty.
- We use the forwardRef concept of the react. which is use to pass the ref to it's child component.
- We create the forwardRef function name Alert.
- call the function as the component inside the Snackbar component.Snackbar provide brief notifications.
- SnackBar are also known as toast.
- export main() component.
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.
import { Box } from "@mui/system";
import Header from "./Header";
export default function Layout({ children }) {
return (
<div>
<Header />
<Box sx={{ mt: 10 }}>{children}</Box>
</div>
);
}
Home.js
- import given components.
- Here we take the container and we render the component which is passed as the children to the layout.js
- below that put your copyright in the
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 is the Navbar of the Dashboard.
- import the AppBar,Toolbar and Typography.
- implement the AppBar which is the navbar component of React Mui.
- after the AppBar implement ToolBar.
- Inside the apply the to display the WEbAtool.
- provide the href for the navigation purpose.
- onClicking on the WEbAtool typography you navigate to the Home page.
- export this component.
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
- This component is consists of dashboard icon dashboard name and navigating system attached to it.
- create variable listConfig array of object with the entities of path,name and color.
- create the constant variable named as color.
- create the function named as GetIcons in which we use switch statement to display the icon on the dashboard.
- pass the name as the parameter to the function as well as for the switch function.
- Now inside the component.
- Use as the parent component. Now apply the map() to the declared array listConfig.
- In the return method of the map() implement with href={item?.path} and key={item?.name}.
- Inside the render inside that render GetIcons function by passing {item?name} as the argument.
- Here covered the left side icons in dashboard with navigation.
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>
);
}
DashBoard.js
- Inside the DashBoard component import the layout which the parent component.
- after that just use for loading message.
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 listItems.
- In right space render the DashBoard.js
- 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 states.
- 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.
- Initialize a function by using React.useCallback hook passing to remove the value from the local storage.
- Initialize the function by using React.useCallback hook for authentication purpose as the token as parameter. call document.cookie=``.
- Initialize the function for getToken();
- Initialize the function for logOut by using React.useCallBack();
- Now just create the function to check whether it is isAuthenticated.
- Now by the help of useEffect load the data which is saved in local storage data.
- By using <AppContext.provider> provide the methods inside the value={{}} which where declared <AppContext.provider>.
- export AppContextWrapper, useAppContext.
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, {});
const addItem = React.useCallback((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 = () => {
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>
);
};
// custom context.
const useAppContext = () => {
return useContext(AppContext);
};
export { AppContextWrapper, useAppContext };
Scan
Chart.js
- import the Chart from the chart.js
- import Pie chart from react-chartjs-2.
- Get the Pie chart sample code from React Mui.
- export this component
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
- In this section of the component we use react-router,material ui,Material Grid.
- import the chart we have designed previously.
- import the context which was created previously i.e useAppContext .
- This component is for displaying the accessibility of the url in the cards and pie chart formate.
- Create the Array of Object in which it consist of the label name and background color which is needed to be displayed on the Card.
- Now, In react-router-dom there is function known as useParams to access the url data. So by using this functionality we can abstract the id of the record.
- Initialize the state using useState name the state as row.
- This state is for counting the number of the records are there.
- Now,Implement the useEffect for fetching the data through api. During the fetch method pass the id to the last url so that we can fetch particular url web accessibility. Set that data into row state.
- extract addItem, removeItem by using object destructuring method from useAppContext.
- create the function handler named as handleAddItem() with name as the parameter initially invoke removeItem().
- removeItem() because if we have to display another url web accessibility in the cards. Then first we have to clear the data which are already present.
- use switch case for display particular issue of the accessibility of url in particular card.
- for example passes message in passes card, Violation issue in Violation Cards vice versa.
- It matches the case and displays the data in particular card.
- after the iteration of the switch case use navigation to travel to another page the page where we get detailed web accessibility.
- Create the function chooseResultsCount with name as parameter use switch case to display the number of the specific issue in the particular card.
- handleViewDetailsClick() function with event and name as parameter in which we call handleAddItem() and pass name as parameter.
- Two display Four cards two horizontal and two vertical and also add the Pie chart in flex manner.
- For this implementation we use Grid system just check the how we have Implemented Grid to manage different child component in the same parent component.
- While checking mainly observe on sx={{}} and xs={} part during implementation of the Grid.
- Get ScanDetails from the row.scanDetails[0].
- pass the scanDetails as props to the pieChart.
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)}
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 all the requirement which are given don't worry about some feature which we have not seen we will this features further.
- Create the array object with the entities of field, headerName,flex,sortable Boolean to check the sorting,valueGetter which is used for the getting the value use the dateFormat(), and renderCell as the callback function which takes params as parameter. Inside the function use use href
- Initialize the state for searchValue , selectionModel,date by using useState().
- Initialize the state for the pagination using useState with default value as pageSize: 5,page: 0.
- To display the data of the records from the database in pagination formate.use POST method on the Api using the useCallBack().
- post page: paginationModel.page, pageSize: paginationModel.pageSize,search: searchValue,field: sortingModel.field, sort: sortingModel.sort, inside the body by stringifying the data.
- In headers section get the token.
- Note this section of component should not be visible to unauthorized person.
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";
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,
]);
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,
]);
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>
);
}