Skip to main content

React Express & MYSQL - Full Stack Product Admin Panel

In the earlier tutorials, you learnt to create React app, and API endpoints to do authentication with JWT, protect APIs, get and count rows of MYSQL database, and upload form data with files to the database in ExpressJs.

In this tutorial, we continue to develop the React app to build a full stack product admin panel website that accesses ExpressJs API endpoints. The product admin panel app will have a left sidebar that allows a visitor easily to navigate different parts of the app. We use bootstrap to style the app. So run the following command to add bootstrap into the React app.

my-app>npm install bootstrap

Our form components are from reactstrap and icons from react-icons packages. Let execute the commands below to install reactstrap and react-icons.

my-app>npm install reacstrap react-icons


To setup the left sidebar in the app, we start by creating the components folder in src folder. To group sidebar files, add sidebar folder to the components folder. We have three sidebar files in the sidebar folder. The SideBar displays SubMenu and the SubMenu uses data from the SideBarData.

sidebar/SideBar.js
import React, { useState } from "react";
import { Link } from "react-router-dom";
import {FaBars} from "react-icons/fa";
import {AiOutlineClose} from "react-icons/ai";
import { SideBarData } from "./SideBarData";
import SubMenu from "./SubMenu";
import { IconContext } from "react-icons/lib";
import { FaSignInAlt,FaSignOutAlt } from "react-icons/fa"

const nav_style = {
    background: "#0a58ca",
    height: "60px",
    display: "flex",
    justifyCcontent: "flex-start",
    alignItems: "center"
};

const navitem_style = {
    color:"#ffffff",
    marginLeft: "auto", 
    marginRight: "20px"
};
  

const navicon_style = {
  marginLeft: "2rem",
  fontSize: "1.8rem",
  display: "flex",
  justifyContent: "flex-start",
  alignItems: "center",
  cursor: "pointer",
};
  

const sidebarwrap_style = {
    width: "100%",
};

  
const Sidebar = (props) => {
  const [sidebaropen, setSidebarOpen] = useState(false);
  const showSidebar = () => setSidebarOpen(!sidebaropen);
  const {token} = props;

  const sidebarnav_style = {
    background: "#0a58ca",
    width: "250px",
    height: "100vh",
    display: "flex",
    justifyContent: "center",
    position: "fixed",
    top: 0,
    left: (sidebaropen ? "0" : "-100%"),
    transition: "350ms",
    zIndex: 10,

};

  const links = [
      {
      id: 2,
      path: token===null? "/login":"/logout",
      text: token===null?"Login":"Logout",
      icon: token===null?<FaSignInAlt/> : <FaSignOutAlt/>
    },

  ]; 
  
  return (
    <>
      <IconContext.Provider value={{ color: "#fff" }}>
        <div style={nav_style}>
          <div to="#" style={navicon_style}>
            <FaBars onClick={showSidebar} />
          </div>

          <h4
            style={{ textAlign: "center", 
                     marginLeft: "30px", 
                     color: "white" }}
          >
            Admin

          </h4>
          <div style={navitem_style}>
          <ul className="navbar-nav mr-auto list-group-horizontal">
             {links.map(link => {
              return (
                <li key={link.id} className="nav-item">
                  <Link to={link.path} className="nav-link">{link.icon}{link.text}</Link>
                </li>
              )
            })}
          </ul> 
          </div>
        </div>
        <div className="nav" style={sidebarnav_style}>
          <div style={sidebarwrap_style}>
            <div to="#" style={navicon_style}>
              <AiOutlineClose onClick={showSidebar} />
            </div>
            {SideBarData.map((item, index) => {
              return <SubMenu item={item} key={index} />;
            })}
          </div>
        </div>
      </IconContext.Provider>
    </>
  );
};
  
export default Sidebar;

sidebar/SideBarData.js

import React from "react";
import {FaPhone,FaEnvelopeOpenText,FaProductHunt,FaServicestack} from "react-icons/fa";
import {AiFillHome} from "react-icons/ai";
import {IoIosPaper,IoMdHelpCircle} from "react-icons/io";
import {RiArrowUpSFill,RiArrowDownSFill} from "react-icons/ri";
  
export const SideBarData = [
    {
        title: "Products",
        path: "/products",
        icon: <FaProductHunt />,
    },
    {
        title: "Events",
        path: "/events",
        icon: <FaEnvelopeOpenText />,
      
        iconClosed: <RiArrowDownSFill />,
        iconOpened: <RiArrowUpSFill />,
      
        subNav: [
          {
            title: "Event 1",
            path: "/events/events1",
            icon: <IoIosPaper />,
          },
          {
            title: "Event 2",
            path: "/events/events2",
            icon: <IoIosPaper />,
          },
        ],
      },
    {
    title: "Services",
    path: "/services",
    icon: <FaServicestack />,
    iconClosed: <RiArrowDownSFill />,
    iconOpened: <RiArrowUpSFill />,
  
    subNav: [
      {
        title: "Service 1",
        path: "/services/services1",
        icon: <IoIosPaper />,
        cName: "sub-nav",
      },
      {
        title: "Service 2",
        path: "/services/services2",
        icon: <IoIosPaper />,
        cName: "sub-nav",
      },
      {
        title: "Service 3",
        path: "/services/services3",
        icon: <IoIosPaper />,
      },
    ],
  },
  
  {
    title: "About Us",
    path: "/about-us",
    icon: <AiFillHome />,
  },
  {
    title: "Contact",
    path: "/contact",
    icon: <FaPhone />,
  },
  
  {
    title: "Support",
    path: "/support",
    icon: <IoMdHelpCircle />,
  },
];

sidebar/SubMenu.js
import React, { useState } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
  
const SidebarLink = styled(Link)`
  display: flex;
  color: #e1e9fc;
  justify-content: space-between;
  align-items: center;
  padding: 20px;
  list-style: none;
  height: 60px;
  text-decoration: none;
  font-size: 18px;
  
  &:hover {
    background: #252831;
    border-left: 4px solid green;
    cursor: pointer;
  }
`;
  
const SidebarLabel = styled.span`
  margin-left: 16px;
`;
  
const DropdownLink = styled(Link)`
  background: #252831;
  height: 60px;
  padding-left: 3rem;
  display: flex;
  align-items: center;
  text-decoration: none;
  color: #f5f5f5;
  font-size: 18px;
  
  &:hover {
    background: green;
    cursor: pointer;
  }
`;
  
const SubMenu = ({ item }) => {
  const [subnav, setSubnav] = useState(false);
  
  const showSubnav = () => setSubnav(!subnav);
  
  return (
    <>
      <SidebarLink to={item.path} 
      onClick={item.subNav && showSubnav}>
        <div>
          {item.icon}
          <SidebarLabel>{item.title}</SidebarLabel>
        </div>
        <div>
          {item.subNav && subnav
            ? item.iconOpened
            : item.subNav
            ? item.iconClosed
            : null}
        </div>
      </SidebarLink>
      {subnav &&
        item.subNav.map((item, index) => {
          return (
            <DropdownLink to={item.path} key={index}>
              {item.icon}
              <SidebarLabel>{item.title}</SidebarLabel>
            </DropdownLink>
          );
        })}
    </>
  );
};
  
export default SubMenu;

In the components folder, create Home component to show welcome screen.
import React from "react"

const Home = (props) => {
   
      return (
        <>
        <div className="container">Welcome!</div>
        </>
     
      )
}
export default Home

Then in App.js file, add SideBar, Home components and  import bootstrap to apply styles to all components of the React app. The App reads a token from local storage and sends the token to SideBar to update to  login states.
src/App.js

import "bootstrap/dist/css/bootstrap.min.css";
import './App.css';
import React, { useState } from "react";
import { BrowserRouter as Router, Routes,Route } from "react-router-dom"
import Sidebar from "./components/sidebar/SideBar";
import Home from './components/Home';


function App() {
// read token from local storage
  const [token,setToken] = useState(localStorage.getItem("token"));
  const handleTokenChange = (tk) =>{
    setToken(tk);
  }
  return (

    <div className="App">
      <Router>
        <Sidebar token={token} />
      <Routes>
        <Route path="/" element ={<Home />}/>
        </Routes>
      </Router>
    </div>

  );
}

export default App;


Save the project. Here is the output after the app reloads:



In the components folder, create Register, Login, and Logout components. The Register component allows a visitor to create a new user account using username, email, and password. We do form validations by allowing the username to start with letters and contain letters and numbers only. The password length must be greater than or equal to 8, and the email must be in the correct format. 
When the registration is successful, the user is redirected to the login screen.

components/register.js

import React, { useState } from "react"
import axios from "axios";
import { useNavigate } from "react-router-dom";

import {
  Button,
  FormGroup,Card,
  CardHeader,CardBody,CardFooter,  
  Input,
  Form,
} from "reactstrap";

const Register = (props) => {

      const [errors,setErrors] = useState({});  
       const [inputs, setInputs] = useState({});
      const history=useNavigate();

      function handleValidation() {
        
        let formIsValid = true;
        let es = {};
        //Name
        if (!inputs.username) {
          formIsValid = false;
          es['username']="can not empty!";
         
        }
    
        if (typeof inputs.username !== "undefined") {
            
          if (!inputs.username.match(/^[a-zA-Z0-9]+$/)) {
            formIsValid = false;
            es['username']="only letters and numbers allowed!";
          }
        }
        // password
        if (!inputs.password) {
            formIsValid = false;
            es['password']="can not empty!";
          }
      
          if (typeof inputs.password !== "undefined") {
            if (inputs.password.length<8) {
              formIsValid = false;
              es['password']="week password!";
            }
          }
    
        //Email
        if (!inputs.email) {
          formIsValid = false;
          es['email']="can not empty!";
        }
    
        if (typeof inputs.email!== "undefined") {
        
          if (!(/\S+@\S+\.\S+/.test(inputs.email))) {
            formIsValid = false;
            es['email']="invalid email!";
          }
        }
        setErrors(es);
        return formIsValid;
      }

      function handleSubmit(e){
        e.preventDefault();
        if(handleValidation()){
            axios({
                method: 'post',
                url: "/api/createuser",
                data: {username: inputs.username,password:inputs.password,email:inputs.email},
                headers: { "Content-type":"application/json",}
            }).then(response=>{

                    try {
                        
                        let dt=JSON.parse(JSON.stringify(response.data));
                        if(dt.message==='success'){
                            history("/login");
                        }
                        else{
                            alert('Failed to create user!');
                        }
                        
                    } catch (e) {
                        alert('Failed to create user!');
                    }
                    
                    
                    
            });
        }
      }
      
      const handleChange = (event) =>{
        const name = event.target.name;
        const value = event.target.value;
        setInputs(values => ({...values, [name]: value}))
      }
      return (
        <div className="container"  style={{width: '18rem'}}>
            <Card
                className="my-2" style={{width: '18rem'}}
            >

            <CardHeader>Register</CardHeader>
            
            <Form className="form">
                <CardBody>
                <FormGroup>
                    <Input
                        type="text"
                        name="username"
                        placeholder="Enter username"
                        value={inputs.username || ""} 
                        onChange={handleChange}
                       
                        />
                    <span style={{color: '#ff2222'}}>{errors.username}</span>
                    </FormGroup>
                   
                    <FormGroup>

                    <Input
                        type="email"
                        name="email"
                        placeholder="Enter email"
                        value={inputs.email || ""} 
                        onChange={handleChange}
    
                        />
                    <span style={{color: '#ff2222'}}>{errors.email}</span>
                    </FormGroup>
                    <FormGroup>

                        <Input
                        type="password"
                        name="password"
                        placeholder="Enter password"
                        value={inputs.password || ""} 
                        onChange={handleChange}
                       
                        />
                    <span style={{color: '#ff2222'}}>{errors.password}</span>
                    </FormGroup>
                </CardBody>   

                <CardFooter>
                    <FormGroup>
        
                    <Button color="primary" onClick={handleSubmit}>Register</Button>
                    </FormGroup>
                </CardFooter>
            </Form>  
            </Card>      
        </div>
      
      )
}
export default Register

Axios is used to access ExpressJs API endpoints. Let install it.
npm install axios

The API endpoint to create a new user in MYSQL database is http://localhost:5000/api/createuser. So we need to proxy from React running port 3000 to ExpressJs server port 5000. By making the proxy from port 3000 to 5000, we do not need to write full API endpoint url. Simply write /api/createuser instead of http://localhost:5000/api/createuser.

To make the proxy work, add proxy to package.json file in the React app.
........ 
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "homepage": ".",
  "proxy": "http://localhost:5000",
.......

The Login component shows a login screen to authenticate the visitor using username and password. The user data is passed to /api/auth API endpoint to be verified. If the verification is successfully, a token will be generated on the server and returned to the Login component. Then the Login component is able to get the token, and save it to local storage. Then, the Login component informs a token change to its parent component (App.js) to update login status in SideBar and redirects the user to products admin panel page. If the login is not successful, the visitor will see an error message at the bottom of the login screen.

components/Login.js
import React, { useState } from "react"
import axios from "axios";
import { useNavigate } from "react-router-dom";
import { Link } from "react-router-dom"
import {
  Button,FormGroup,Card,CardHeader,
  CardBody,CardFooter,  
  Input,Form,Label,
} from "reactstrap";

const Login = (props) => {
      const [username, setUsername]=useState('');
      const [password,setPassword]=useState('');
      const [logerr,setLogError] = useState('');  
      const {tokenchange} = props;
      const history=useNavigate();
      
      function handleTokenChange(tk){
            tokenchange(tk);
      }

      function handleSubmit(e){
        e.preventDefault();
   
        // Make the POST call to login API end point
        axios({
            method: 'post',
            url: "/api/auth",
            data: {username: username,password:password},
            headers: { "Content-type":"application/json",}
        }).then(response=>{
                console.log("token",response.data);
   
                try {
                    let dt=JSON.parse(JSON.stringify(response.data));
                    if(dt.message==='success'){

                        localStorage.setItem("token", dt.token);
                        handleTokenChange(dt.token);
                        history("/products");
                    }
                    else{
                        setLogError(dt.message);
                    }
                    
                } catch (e) {
                    setLogError('Invalid user!');
                }
                
                
                
        });
      }
      const handleUsernameInputChange = (event) => {
        setUsername(event.target.value);
      }
      const handlePasswordInputChange = (event) => {
        setPassword(event.target.value);
      }
      return (
        <div className="container"  style={{width: '25rem'}}>
            <Card
                className="my-2" style={{width: '25rem'}}
            >

            <CardHeader>Login</CardHeader>
            
            <Form className="form">
                <CardBody>
                    <FormGroup>

                    <Input
                        type="text"
                        name="username"
                        placeholder="Enter username"
                        value={username}
                        onChange={(e) =>handleUsernameInputChange(e)}
                        required
                        />
        
                    </FormGroup>
                    <FormGroup>

                        <Input
                        type="password"
                        name="password"
                        placeholder="Enter password"
                        value={password}
                        onChange={(e) =>handlePasswordInputChange(e)}
                        required
                        />
        
                    </FormGroup>
                </CardBody>   

                <CardFooter>
                    <FormGroup>
        
                    <Button color="primary" onClick={handleSubmit}>Login</Button>
                    <Label style={{marginLeft: '5px'}}>Don't have an account?</Label>  <Link to="/register">create user</Link>
                    <Label className="text-danger">{logerr}</Label>
                    
                    </FormGroup>
                </CardFooter>
            </Form>  
            </Card>      
        </div>
      
      )
}
export default Login

The Logout simply removes the stored token from the local storage and redirect the user to the Home page.
components/Logout.js
import React,{useEffect} from "react"
import { useNavigate } from "react-router-dom";

const Logout = (props) => {

      const {tokenchange} = props; 
      const history=useNavigate();
      function handleTokenChange (tk) {
          tokenchange(tk); // inform token change to update logout status in SideBar
      }

      useEffect(() => {
        localStorage.removeItem('token');
        handleTokenChange(null);
        history("/");
         }, []);

      return (
        <><div>Logout work</div></>
      
      )
}
export default Logout

The ProductMain component is the product admin panel that allows an authorized user to manage products. He/she can view products list, sort the products by clicking the column headers, search for products, add new product with images, update, and delete products from the MYSQL database. To access restricted APIs, you need to pass the token via axios request headers to the sever. If the token is not valid, subsequent requests will fail. 

components/ProductMain.js
import React,{ useState, useEffect } from "react";
import axios from "axios";
import SearchBar from "./SearchBar";
import { FaTrash } from "react-icons/fa"
import { FaEdit } from "react-icons/fa"
import { NavLink, Navigate } from "react-router-dom";
import ProgressBar from "./ProgressBar";

import {
    Label,Form,FormGroup,Input,
    Button,Col,Table,
    Modal, ModalHeader, ModalBody, ModalFooter,
  } from "reactstrap";


// global variables
var start=0;
var step=5;
var search='';

function ProductMain(props) {
    //vairables/
    const [products,setProducts]=useState(null);
    const [modal,setModal]=useState(false);
    const [id,setId]=useState(0);
    const [title,setTitle]=useState('');
    const [description,setDescription]=useState('');
    const [price,setPrice]=useState(0.0);
    const [document,setDocument]=useState(null);
    const [refresh,setRefresh]=useState(false);
    const [sorted,setSorted]=useState(false);
    const [isEditMode,setIsEditmode]=useState(false);
    const [numRows,setNumRows]=useState(0);
    const hstyle={textDecoration: 'none'};
    const [files,setFiles]=useState(null);
    const [progressing,setProgressing]= useState(false);
    const token= localStorage.getItem("token");
    
    const pstyle={
      marginRight: '20px',
      textDecoration: 'none',
     };
   
    useEffect(() => {
      axios
     
      .get("/api/products?search="+search+"&start="+start+"&step="+step,{
        headers:{
            
            "authorization": `Bearer ${token}`,
        },

       
      })
      .then((res) => {
        parseResult(res.data);
       
      
      }) 
      .catch((err) => console.log(err));
     }, [refresh]);


     const handleSubmit = (e) => {
      e.preventDefault();
  
      let form_data = new FormData();
      form_data.append('id', id);
      form_data.append('title', title);
      form_data.append('description', description);
      form_data.append('price',price);
      if(document!=null){
        for (let i = 0; i < document.length; i++) {
          
          form_data.append('document', document[i],document[i].name);
          
        }
      }

         for (var key of form_data.entries()) { 
        console.log(key[0] + ', ' + key[1]);
      }

      setProgressing(true);
        // Make the POST call by passing a config object to the instance
      axios({
        method: isEditMode?'put':'post',
        url: "/api/products",
        data: form_data,
        headers: { "Content-type":"application/json",
        "authorization": `Bearer ${token}`,
        }
      }).then(res=>{
        console.log("res=",res);
        let dt=JSON.parse(JSON.stringify(res.data));
        if(dt.message==='success'){
          if(search!=='') searchNow(search);
          else setRefresh(!refresh); 
          setProgressing(false);
          toggle();
        }
        else{
          if(isEditMode)
            alert("Failed to update record");
          else 
            alert("Failed to insert record");  
        }
      });

    };


    const searchNow= (q) =>{
   
            start=0; // reset offset
            search=q;
            axios({
              method: 'get',
              url: "/api/products?search="+search+"&start="+start+"&step="+step,
              headers: { "Content-type":"application/json","authorization": `Bearer ${token}`}
            }).then(response=>{

              parseResult(response.data);
              
            });
        
      };
    const deleteProduct = (id) => {
      axios({

        url: "/api/products/"+id+"/",
        headers: { "Content-type":"application/json","authorization": `Bearer ${token}`}
      }).then(response=>{
        console.log("deleted",response.data);
        setRefresh(!refresh); 
        
      });
      };  
    const editProduct = (item) => {
       
       let imgs= products
        .filter(product => {
          return (
            product.id === item.id
          );
        }).map((product,index) =>{

          return(product.Images);
        });

        setFiles(imgs[0]);
        setIsEditmode(true);
        setId(item.id);
        setTitle(item.title);
        setDescription(item.description);
        setPrice(item.price);
        toggle(!modal);
      };    
    function handleChangeTitle(e){
      setTitle(e.target.value);
    }  
    function handleChangeDescription(e){
      setDescription(e.target.value);
    }  
    function handleChangePrice(e){
      setPrice(e.target.value);
    }  
    
  function toggle() {

    setModal(!modal);
    

  }
  function openAddNewModel() {
    setIsEditmode(false);
    setFiles(null);
    
    toggle();

  }
  const handleImageChange = (e) => {
    setDocument(e.target.files);
  };
  const handlePaging = (mv) =>{
      if(mv==='next') {
        if(start<numRows-step) start=start+step;
      }
      else if(mv==='prev') {
        if(start>=step) start=start-step;
      
      }
 
       axios({
        method: 'get',
        url: "/api/products?search="+search+"&start="+start+"&step="+step,
        headers: { "Content-type":"application/json","authorization": `Bearer ${token}`}
      }).then(response=>{
      
        parseResult(response.data);
        
      });

  }
  const parseResult = (data) =>{
    let dt=JSON.parse(JSON.stringify(data));
    //console.log("dt=",data);
    if(dt.message==='success'){
      setProducts(dt.data);
      setNumRows(dt.numrow);
    }
}
  const handleHeaderClick = (field) =>{
     // sort records
     if(field!=='price') products.sort((a,b) => a[field].localeCompare(b[field]));
     else products.sort((a,b) => b[field]-a[field]);
     // refresh UI
     setSorted(!sorted);
    

      
  }

  const MAX_PERCENTAGE = 100;
  const MIN_PERCENTAGE = 45;
  const [percentage, setPercentage] = React.useState(MAX_PERCENTAGE);
  useEffect(() => {
    const timeout = setTimeout(() => {
      setPercentage(
        percentage === MAX_PERCENTAGE ? MIN_PERCENTAGE : MAX_PERCENTAGE
      );
    }, 2000);

    return () => {
      window.clearTimeout(timeout);
    };
  }, [percentage]);

    if(token===null) return <Navigate to="/login" />

    return (
      <>
    <SearchBar searchNow={searchNow}/>
    
    <div className="container">
     
      <h1>Products</h1> 
      
      {
      <Table striped bordered hover>
      <thead>
      <tr><th><NavLink style={hstyle} to={"#"} onClick={() => handleHeaderClick("title")}>Title</NavLink></th><th ><NavLink style={hstyle} to={"#"} onClick={() => handleHeaderClick("description")}>Description</NavLink></th><th><NavLink style={hstyle} to={"#"} onClick={() => handleHeaderClick("price")}>Price</NavLink></th><th colSpan={2}></th></tr>  
      </thead>
      <tbody>
      {
       products && products.map((item, i) => {
        return (
            <tr key={item.id}>
            <td >{item.title}</td>
            <td >{item.description}</td>
            <td >{item.price}</td>
            <td > <Button color="primary"  onClick={() =>editProduct(item)}>
            <FaEdit style={{ color: "white", fontSize: "12px" }} />

            </Button></td>
            <td > <Button color="primary" onClick={() =>deleteProduct(item.id)}>
            <FaTrash style={{ color: "red", fontSize: "12px" }} />

            </Button></td>
            </tr>
        );	

         })
      }
      </tbody>
    </Table> 
    }
    {

      (start-step>=0 && numRows>step) && <NavLink onClick={() =>handlePaging('prev')} style={pstyle} to={"#"}>{"<"}</NavLink>
      
      }  
    {

     (numRows-start>=step  && numRows>step) && <NavLink abc={numRows} onClick={() =>handlePaging('next')}   style={pstyle} to={"#"}>{">"}</NavLink>  
    }
    <div style={{float: 'right'}}>
      <Button color="primary"  onClick={openAddNewModel}>Add New</Button>
    </div>
    <Modal isOpen={modal} toggle={toggle}>
          <ModalHeader toggle={toggle}>{isEditMode?'Edit Product':'Add Product'}</ModalHeader>
          <ModalBody>
            
        <Form>
        <FormGroup row>
          <Label for="title" sm={2} size="lg">Title</Label>
          <Col sm={10}>
            <Input type="text" value={title} name="title" id="title" placeholder="Title" bsSize="lg" onChange={(e) =>handleChangeTitle(e)} required />
          </Col>
        </FormGroup>
        <FormGroup row>
          <Label for="description" sm={2}>Description</Label>
          <Col sm={10}>
            <Input type="textarea" value={description} name="description" id="description" placeholder="Description"  onChange={(e) =>handleChangeDescription(e)} required />
          </Col>
        </FormGroup>
        <FormGroup row>
          <Label for="price"  sm={2}>Price</Label>
          <Col sm={10}>
            <Input type="number" value={price} name="price" id="price" placeholder="Price"  onChange={(e) =>handleChangePrice(e)} required />
          </Col>
        </FormGroup>
        <FormGroup>
            <p><Label for="file">File</Label>
              <Input 
                multiple
                type="file"
                id="document"
                name="document"
				        accept="image/png, image/jpeg"  onChange={handleImageChange} 
              
              />
              {
                files && files.map((item, i) =>{
                  return(
                   
                    <img style={{marginRight: "5px", width: "50px",height: "50px"}} src={`http://localhost:5000/${item.fileUrl}`} />
                   
                  );
                })
              }
              </p>
              <p>
              {
                  progressing &&
                  <ProgressBar bgcolor={"#6a1b9a"} completed={percentage} />
                  }
              </p>
            </FormGroup>
      </Form>

          </ModalBody>
          <ModalFooter>
            <Button color="primary" onClick={handleSubmit}>Ok</Button>
            <Button color="secondary" onClick={toggle}>Cancel</Button>
          </ModalFooter>
        </Modal>
      
    </div>
    </>
  );
}

export default ProductMain;

components/ProgressBar.js displays a progress bar when the form data is submitted. It lets the user know what is going on and wait until the form is uploaded completely.

const ProgressBar = (props) => {
    const { bgcolor, completed } = props;
 
    const containerStyles = {
      height: 20,
      width: '200px',
      backgroundColor: "#e0e0de",
      borderRadius: 50,
      margin: 50
    }
 
    const fillerStyles = {
      height: '100%',
      width: `${completed}%`,
      backgroundColor: bgcolor,
      transition: 'width 1s ease-in-out',
      borderRadius: 'inherit',
    }
 
    const labelStyles = {
      padding: 5,
      color: 'white',
      fontWeight: 'bold'
    }
 
    return (
      <div style={containerStyles}>
         
        <div style={fillerStyles}>
          <span style={labelStyles}>Uploading...</span>
        </div>
      </div>
    );
  };
 
  export default ProgressBar;

Finally, add the Register, Login, Logout, and ProductMain components to App.js.

import "bootstrap/dist/css/bootstrap.min.css";
import './App.css';
import React, { useState } from "react";
import ProductMain from './components/ProductMain';
import { BrowserRouter as Router, Routes,Route } from "react-router-dom"
import Sidebar from "./components/sidebar/SideBar";
import Home from './components/Home';
import Login from './components/login';
import Register from './components/register';
import Logout from './components/logout';


function App() {
  const [token,setToken] = useState(localStorage.getItem("token"));
  const handleTokenChange = (tk) =>{
    setToken(tk);
  }
  return (

    <div className="App">
      <Router>
        <Sidebar token={token} />
      <Routes>
        <Route path="/" element ={<Home />}/>
        <Route path="/products" element ={<ProductMain />}/>
        <Route path="/login" element ={<Login tokenchange={handleTokenChange} />}/>
        <Route path="/register" element ={<Register />}/>
        <Route path="/logout" element ={<Logout tokenchange={handleTokenChange} />}/>
      </Routes>
      </Router>
    </div>

  );
}

export default App;


Save project. Congratulation! You have developed a full stack website using React Express & MYSQL.





Video Demo

Comments

Popular posts from this blog

Get start with React

To start web development with React, i recommend you install Node. Node comes with NPM (package manager) helps you easy to add dependencies, create, start, and build your React apps. You can download and install NPM from its official web site: Download NPM . Another useful tool for coder is Visual Studio Code . On my Windows machine, i have installed Node version 16.10.0 with NPM 7.14.0. Now create a project folder and name it react_tutorials in Drive D: or other drive of your choice. Then open the folder in Visual Studio COde (VSC). From the VSC editor, select Terminal. In the terminal, enter the following command to create my-app app in the project folder created above. D:\react_tutorials>npm init react-app my-app After the my-app app is created successfully. You change to my-app folder (cd my-app) . Then You run " npm start " command to start your first react app on browser. React automatically starts on port 3000.

Why React?

React is one of the top UI development frameworks. It is very popular today among web developers around the globe. I love React because of the following things: - It is simple to learn and use. - It is JavaScript library. - It effectively applies changes to a web page without reloading the page while data changed. - It is easy to find problems or errors while you are coding.