React App - sandeep-g-more/webatool GitHub Wiki
npm install react-router-dom
npm install react-chartjs-2
npm install @mui/material
npm install @mui/x-data-grid-generator
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.
-
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" },
];
- 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>
- 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>
);
}
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>
);
}
- The component represents the plain text which gives brief idea about our application.
- 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>
- 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.
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>
</>
);
}
-
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>
)}
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> */}
</>
);
}
- 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.
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>
</>
);
}
- 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>
)}
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>
);
}
- 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>
);
}
- 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.
- 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>
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>
);
}
- 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>
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>
);
}
- 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>
);
}
- 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.
- 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 };
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 };
- 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
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 }}
/>
);
}
- 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>
);
}
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>
);
}
- 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>
);
}
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>
);
}
-
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
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>
);
}
-
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;
}
};
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;
}
};
- 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;
- By using the conditional rendering we have created both signUp and sigIn page in single component.
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>
);
}