ShopKart Shopping Cart & LocalStorage

Business Scenario

Hello talented developers!

ShopKart has successfully implemented its product listing and filtering functionality. Customers can now browse products and find items they are interested in purchasing.

What’s Already Working?

Product listing and filtering are implemented.Customers can now browse products and find items they are interested in purchasing

The Challenge

Add to Cart buttons are currently only part of the product interface.

Customers need a proper shopping cart where they can:

  • Review selected products
  • Modify quantities
  • Remove unwanted items
  • See the total amount before checkout

In this lab, students will transform the existing ShopKart product-selection experience into a fully functional shopping cart.

Pre-Lab Preparation

Module:

1) Handling Side-Effects

2) Handling Forms in React

git pull origin branchName

Git Pull

Task 1 : Create the Cart Page Structure

  • We already have a Cart.jsx page in the project, but it is currently only a placeholder.

Open Cart.jsx and  create the initial cart structure.

1

  • We will replace it with the actual ShopKart Cart page.

Style the Cart Page

4

    <li>
      <a className="dropdown-item" href="/register">Register</a>
    </li>

  </ul>

</div>

Style the Account Section

4

  • Now, the navbar will look like this :

Task 2 : Connect Login and Register Pages

  • We already have Login.jsx and Register.jsx
  • But, Currently they are simple placeholder components.
  • We need to connect them to:

/login
/register

In App.jsx, add:

1

import Login from "./pages/Login";
import Register from "./pages/Register";

Then :

<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />

Plan the Login Form Workflow

2

  • Before creating the Login form, we first understand how the Login feature is expected to work.
  • When a user wants to log in to ShopKart, the following process will take place:

User opens Login page

Enters username and passwords

Submits form

Form validation runs

Username is passed using setUserName()

User is redirected to Home

Navbar displays "Hello, Username"

Task 3 :  Create the Login Form

Open Login.jsx and add imports:

1

import React from "react";
import { useForm } from "react-hook-form";

And inside the component add :

const { register, handleSubmit, formState: { errors }} = useForm();

useForm() provides the functionality required to:

  • Register form inputs
  • Handle form submission
  • Validate inputs
  • Access validation errors

Create the Login Form with Validation

2

import React from "react";
import { useForm } from "react-hook-form";
import "./Login.css";

function Login({ setUserName }) {
  const { register, handleSubmit, formState: { errors }} = useForm();

  return (
    <div className="login-page">
      <div className="login-container">

        <div className="login-logo">
          <img src="/images/logo.png" alt="ShopKart" />
        </div>

        <h2>Welcome Back!</h2>
        <p className="login-subtitle"> Login to continue shopping with ShopKart </p>

        <form onSubmit={handleSubmit(onSubmit)}>
          <div className="form-group">
            <label>Username</label>
             <input type="text" placeholder="Enter your username"
              {...register("username", {
                required: "Username is required" })} />

            {errors.username && (
              <p className="form-error">
                {errors.username.message}
              </p>
            )}
          </div>

          <div className="form-group">
            <label>Password</label>

            <input type="password" placeholder="Enter your password"
              {...register("password", {
                required: "Password is required",
                minLength: {
                  value: 6,
                  message: "Password must be at least 6 characters"
                }
              })}
            />
            {errors.password && (
              <p className="form-error">
                {errors.password.message}
              </p>
            )}
          </div>
            <button type="submit"> Login </button>
        </form>

        <p className="register-text"> Don't have an account? <a href="/register"> 
          Register</a> </p>

      </div>
    </div>
  );
}
export default Login;

Why are we using setUsername ?

  • The Login component receives setUserName as a prop.
  • When the user submits the form:
  • The entered username is sent to the parent component so that it can be used elsewhere in the application.
setUserName(data.username);

Handle successful form submission

2

const onSubmit = (data) => {
    setUserName(data.username);
  };

Style the login form

3

Task 4 :  Create the Registeration Form

Open Register.jsx and add :

1

import React from "react";
import { useForm } from "react-hook-form";
import "./Register.css";

function Register({ setUserName }) {
  const { register,handleSubmit,formState: { errors }} = useForm();
  };

 return (
    <div className="register-page">
      <div className="register-container">
        <div className="register-logo">
          <img src="/images/logo.png" alt="ShopKart"/>
        </div>
         <h2>Create Your Account</h2>
        <p className="register-subtitle"> Register to start shopping with ShopKart </p>
        <form onSubmit={handleSubmit(onSubmit)}>
          <div className="form-group">
            <label>Full Name</label>

            <input type="text" placeholder="Enter your full name"
              {...register("fullName", {
                required: "Full name is required"
              })}
            />

            {errors.fullName && (
              <p className="form-error"> {errors.fullName.message}</p>
            )}
          </div>
           <div className="form-group">
            <label>Username</label>
            <input type="text" placeholder="Enter your username"
              {...register("username", {
                required: "Username is required"
              })}
            />

            {errors.username && (
              <p className="form-error"> {errors.username.message}</p>
            )}
          </div>
         <div className="form-group">
            <label>Email</label>
           <input type="email" placeholder="Enter your email"
              {...register("email", {
                required: "Email is required"
              })}
            />
            {errors.email && (
              <p className="form-error">
                {errors.email.message}
              </p>
            )}
          </div>
         <div className="form-group">
            <label>Password</label>

            <input type="password" placeholder="Create a password"
              {...register("password", {
                required: "Password is required",
                minLength: {
                  value: 6,
                  message: "Password must be at least 6 characters"
                }
              })}/>
         
            {errors.password && (
              <p className="form-error">{errors.password.message} </p>
            )}
          </div>
            <div className="form-group">
            <label>Confirm Password</label>

            <input type="password" placeholder="Confirm your password"
              {...register("confirmPassword", {
                required: "Please confirm your password"
              })}
            />

            {errors.confirmPassword && (
              <p className="form-error"> {errors.confirmPassword.message}  </p>
            )}
          </div>

          <button type="submit"> Register </button>

        </form>

        <p className="login-text"> Already have an account? <a href="/login"> Login</a>
        </p>

      </div>

    </div>
  );
}

export default Register;

Handle successful form submission

2

const onSubmit = (data) => {

    if (data.password === data.confirmPassword) {
      setUserName(data.username);
    } else {
      alert("Passwords do not match");
    }

  };

Why are we using if...else here ?

We use if...else to compare the Password and Confirm Password values after the

other form validations have passed.

Style the Register form

3

Task 5 :  Introduce useState

1

import React, { useState } from "react";
  • Now we need a place to store the username after login/register.
  • We will use useState in App.jsx because the username needs to be available to both the Login/Register pages and the Navbar.

Add this import at the top

2

Inside App() add:

const [userName, setUserName] = useState("");

3

Connect Login & Register Using setUserName

Now we pass setUserName from App.jsx to Login and Register.

Change:

<Route path="/login" element={<Login />} />

<Route path="/register" element={<Register />} />

To :

<Route path="/login" element={<Login setUserName={setUserName} />}/>
<Route path="/register" element={<Register setUserName={setUserName} />} />

Task 6 :  Use useNavigate for Redirect

  • We want the user to go back to Home after successful Login/Register.

1

Open Login.jsx and import useNavigate

import { useNavigate } from "react-router-dom";
  • For Login page :

2

Initialize the useNavigate hook inside your component.

const navigate = useNavigate();
const onSubmit = (data) => {
  setUserName(data.username);
  navigate("/");
};

3

Update form submission

  • For Register page :

1

Open Register.jsx and import useNavigate

const navigate = useNavigate();

2

Initialize the useNavigate hook inside your component.

import { useNavigate } from "react-router-dom";

3

Update form submission

const onSubmit = (data) => {
  
  if (data.password === data.confirmPassword) {
    setUserName(data.username);
    navigate("/");
  } else {
    alert("Passwords do not match");
  }
};

Task 7 :  Render Navbar from App.jsx

  • Earlier, Navbar was rendered inside Home.jsx and Products.jsx.
  • Now we are moving the common Navbar to App.jsx.

Why are we doing this?

  • From Lab 5 onwards, App.jsx contains :

const [userName, setUserName] = useState("");

  • The Navbar also needs this same userName to display:
  • The Login and Register pages update this state.

Hello, Username

  • If Navbar stays inside Home.jsx, we would have to pass userName:

App.jsx(userName) --> Home.jsx(userName) --> Navbar.jsx

  • Instead, we can directly pass it:

App.jsx

          userName state

          Navbar -->  Hello, Username

  • This makes the data flow simpler.

1

Update App.jsx

import Navbar from "./components/Navbar";
  • Add the Navbar import:
  • Then render Navbar above <Routes>
 <Navbar userName={userName} />
<Routes>
---
</Routes>

2

Remove Navbar from Home and  Products

3

Receive userName in Navbar

  • Open Navbar.jsx and change :

function Navbar() {

  • To :

function Navbar({userName}) {

Task 8 :  Display Greeting

1

Open Navbar.jsx - In your account/profile section, use:

{/* Account / Greeting */}
{userName ? (
  <span className="user-greeting">
    <img src="/icons/profile-icon.png" alt="Profile" className="greeting-profile-icon" />
    Hello, {userName}
  </span>
) : (
  <div className="dropdown account-dropdown">
    <button className="account-btn" type="button" data-bs-toggle="dropdown" 
      aria-expanded="false" aria-label="My Account">
      <img src="/icons/profile-icon-2.png" alt="My Account" className="profile-icon" />
    </button>

    <ul className="dropdown-menu account-menu">
      <li><a className="dropdown-item" href="/login">Login</a></li>
      <li><a className="dropdown-item" href="/register">Register</a></li>
    </ul>
  </div>
)}

2

Style the Greetings section

Task 9 : Use React Router Links for Navbar Navigation

  • We are replacing normal internal <a href=""> links with React Router's Link and NavLink.

Why are we doing this?

  • Normal links can reload the entire React application. When the application reloads, the userName stored in useState is reset.
  • React Router links allow us to move between pages without reloading the application.

1

In Navbar.jsx import :

import { Link, NavLink } from "react-router-dom";

2

Use NavLink for the main page navigation:

<NavLink className="nav-link" to="/"> Home </NavLink>
<NavLink className="nav-link" to="/products"> Products </NavLink>
<NavLink className="nav-link" to="/about"> About </NavLink>
<NavLink className="nav-link" to="/contact"> Contact </NavLink>
<NavLink className="nav-link" to="/cart"> Cart </NavLink>

3

Use Link for the logo , login and register

<Link className="navbar-brand" to="/">
   <img src="/images/logo.png" alt="ShopKart" className="shopkart-logo"/>
</Link>
<Link className="dropdown-item" to="/login"> Login </Link>
<Link className="dropdown-item" to="/register"> Register </Link>

4

Add this at the end of Navbar.css

.navbar .nav-link.active {
  color: #0aa89e;
  position: relative;
}

.navbar .nav-link.active::after {
  content: "";
  position: absolute;
  left: 5px;
  right: 5px;
  bottom: 2px;
  height: 2px;
  background-color: #0aa89e;
  border-radius: 2px;
}

Task 10 : Connect Trending Link to Home Section

The Trending 🔥 Navbar link is not a separate page. It points to the Trending Products section inside the Home page.

  • If we are already on Home, the navigation works

But the problem is: If we are on the Products page and click the Trending link, nothing happens.

  • This happens because the #trending section does not exist on the Products page.

Solution :

We will make the link:

If already on Home → scroll to Trending

If on another page → go to Home → scroll to Trending

1

Update the Navbar.jsx import

import { Link, NavLink, useNavigate } from "react-router-dom";

Then inside the Navbar :

function Navbar({ userName }) {
  const navigate = useNavigate();

  // existing code
}

2

Now Replace the Trending link with:

<a
  className="nav-link"
  href="#trending"
  onClick={(e) => {
    e.preventDefault();

    if (window.location.pathname === "/") {
      document.getElementById("trending")?.scrollIntoView({behavior: "smooth"});
    } 
else {
      navigate("/");

      setTimeout(() => {
        document.getElementById("trending")?.scrollIntoView({
          behavior: "smooth"
        });
      }, 100);
    }
  }}
>
  Trending 🔥
</a>
  1. if (window.location.pathname === "/")  :
  • Checks the current URL path.
  • If the path is: "/"  the user is already on the Home page.

2. document.getElementById("trending")?.scrollIntoView({ behavior: "smooth" });

3. else { navigate("/");

  • If the user is on: "/products" or another page, we first navigate them back to:"/"
  • This finds: id="trending" and scrolls to it.
  • The important part is: behavior: "smooth" which makes the scrolling animated instead

     of jumping instantly.

4. setTimeout(() => {

  • React needs a little time to render the Home page and its elements. so we wait 100 milliseconds before trying to find #trending.

5. document.getElementById("trending")?.scrollIntoView({ behavior: "smooth" });

  • Once Home has rendered, this finds the Trending section and smoothly scrolls to it.

We are done with this lab. The latest source code has been uploaded to GitHub. You can access the latest commit using the link below: 

 

Great job!

Your ShopKart Login, Registration & Forms are now working smoothly with validation, navigation.

Checkpoint

   Git Push

git push origin branchName

Next-Lab Preparation

Module:

1) Handling Side-Effects

2) React Context & Reducers