In this episode, you will:


Part 1: Persistent Login & Auto Logout

1. Persistent Login (Already Done with LocalStorage)

Since we already save user info in localStorage, the session persists between refreshes.
Now, we’ll add a loginTime and manage session expiration.


2. Save Login Time

Update the login logic in Login.jsx:

1
2
localStorage.setItem("user", JSON.stringify(user));
localStorage.setItem("loginTime", Date.now());

3. Enhance PrivateRoute.jsx to Check Timeout

Update src/components/PrivateRoute.jsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { Navigate } from "react-router-dom";

const SESSION_TIMEOUT = 10 * 60 * 1000; // 10 minutes

const PrivateRoute = ({ children }) => {
  const user = JSON.parse(localStorage.getItem("user"));
  const loginTime = parseInt(localStorage.getItem("loginTime"));

  const now = Date.now();
  const expired = now - loginTime > SESSION_TIMEOUT;

  if (!user || expired) {
    localStorage.removeItem("user");
    localStorage.removeItem("loginTime");
    return <Navigate to="/login" />;
  }

  return children;
};

export default PrivateRoute;

4. Optional: Show Logout Countdown

Inside Navbar.jsx, show how long before session expires:

1
2
const loginTime = parseInt(localStorage.getItem("loginTime"));
const remaining = Math.max(0, 10 * 60 - Math.floor((Date.now() - loginTime) / 1000));

You can render this with:

1
<span className="text-sm text-gray-200">Session: {secondsLeft}s</span>
  1. Test the Flow

Part 2: Polish UI with TailwindCSS

1. Product Card Styling

In ProductCard.jsx:

1
2
3
4
5
6
7
8
<div className="bg-white rounded-2xl shadow-md p-4 flex flex-col hover:shadow-lg transition">
  <img src={product.image} className="w-full h-48 object-cover rounded-lg mb-4" />
  <h2 className="text-lg font-semibold line-clamp-2">{product.title}</h2>
  <p className="text-blue-600 font-bold mt-2">${product.price}</p>
  <button className="mt-auto bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">
    Add to Cart
  </button>
</div>

2. Responsive Grid in Home.jsx

1
2
3
4
5
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
  {products.map(product => (
    <ProductCard key={product.id} product={product} />
  ))}
</div>

3. Cart Item Styling in Cart.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div className="border p-4 rounded-xl flex flex-col sm:flex-row justify-between gap-4 items-center">
  <div>
    <h2 className="font-semibold text-lg">{item.title}</h2>
    <p className="text-gray-600">${item.price}</p>
    <div className="flex items-center gap-2 mt-2">
      <button className="bg-gray-200 px-2 py-1 rounded" onClick={...}></button>
      <span>{item.quantity}</span>
      <button className="bg-gray-200 px-2 py-1 rounded" onClick={...}>+</button>
    </div>
  </div>
  <div className="text-right">
    <p className="font-bold">${(item.price * item.quantity).toFixed(2)}</p>
    <button className="text-red-500 mt-2 hover:underline" onClick={...}>Remove</button>
  </div>
</div>

4. Responsive Navbar

In Navbar.jsx:

1
2
3
4
5
6
7
8
<nav className="flex items-center justify-between p-4 bg-blue-600 text-white flex-wrap">
  <div className="text-xl font-bold">E-Shop</div>
  <div className="space-x-4 mt-2 sm:mt-0">
    <Link to="/" className="hover:underline">Home</Link>
    <Link to="/cart" className="hover:underline">Cart</Link>
    <Link to="/login" className="hover:underline">Login</Link>
  </div>
</nav>