Tuesday, May 6, 2025

REACTTS : ROUTING - AUTHENTICATION- AUTHORIZATION - DATA BINDING

1- NAVIGATION - NavBar.tsx

import React from 'react';
import logo from '../assets/faith.png'; // <-- Make sure this image exists in your assets folder

interface NaviBarProps {
    userName: string;
    onLogout: () => void;
}

const NaviBar: React.FC<NaviBarProps> = ({ userName, onLogout }) => {
    return (
        <nav className='navbar navbar-expand-lg navbar-dark bg-dark py-3'>
            <div className='container-fluid'>
                <span className='navbar-brand d-flex align-items-center'>
                    <img
                        src={logo}
                        alt="EMS v2025"
                        width="40"
                        height="40"
                        className="me-2"
                    />
                    EMS v2025
                </span>
                <button
                    className='navbar-toggler'
                    type='button'
                    data-bs-toggle='collapse'
                    data-bs-target='#navbarNavDropdown'
                    aria-controls='navbarNavDropdown'
                    aria-expanded='false'
                    aria-label='Toggle Navigation'
                >
                    <span className='navbar-toggler-icon'></span>
                </button>
                <div className='collapse navbar-collapse' id='navbarNavDropdown'>
                    <ul className='navbar-nav me-auto'>
                        <li className='nav-item'>
                            <a className='nav-link' href='#'>Home</a>
                        </li>
                        <li className='nav-item'>
                            <a className='nav-link' href='#'>Search Books</a>
                        </li>
                    </ul>
                    <ul className='navbar-nav ms-auto'>
                        <li className='nav-item d-flex align-items-center me-2 text-white'>
                            <i className="bi bi-person-circle me-1"></i>
                            {userName}
                        </li>
                        <li className='nav-item'>
                            <button
                                className='btn btn-outline-light'
                                onClick={onLogout}
                            >
                                Logout
                            </button>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    );
};

export default NaviBar;

2- LOGIN - Login.tsx

import React, { useState } from 'react';
import axios from '../servicess/api'; // or import api from '../api'
import { useNavigate } from 'react-router-dom';

interface LoginProps {
    onLoginSuccess: (username: string) => void;
}

const Login: React.FC<LoginProps> = ({ onLoginSuccess }) => {
    const [userName, setUserName] = useState('');
    const [password, setPassword] = useState('');
    const [error, setError] = useState('');
    const navigate = useNavigate();

    const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault();

        if (!userName.trim() || !password.trim()) {
            setError('Username and password are required.');
            return;
        }

        try {
            const response = await axios.get(`/users/${userName}&${password}`);
            const user = response.data.data;
            const token = user.ACCESSTOKEN;
            const roleId = user.role;
            const username = user.UserName;
            console.log(user);
            console.log(token);
           
            if (token) {
                localStorage.setItem('authToken', token);
                localStorage.setItem('userName', username);
                localStorage.setItem('roleId', roleId);
                onLoginSuccess(username);

                const roleIdStr = String(roleId); // force to string

                // Navigate based on roleId
                if (roleIdStr === '1') {
                    navigate('/admin');
                } else if (roleIdStr === '2') {
                    navigate('/manager');
                } else if (roleIdStr === '3') {
                    navigate('/receptionist');
                } else {
                    navigate('/unauthorized');
                }
            } else {
                setError('Invalid login credentials.');
            }
        } catch (err) {
            setError('Invalid login credentials.');
        }
    };

    return (
        <div className="container mt-5">
            <div className="row justify-content-center">
                <div className="col-md-4">
                    <div className="card p-4">
                        <h2 className="text-center mb-4">Login</h2>
                        <form onSubmit={handleSubmit}>
                            <div className="mb-3">
                                <label className="form-label">Username</label>
                                <input
                                    type="text"
                                    className="form-control"
                                    value={userName}
                                    onChange={(e) => setUserName(e.target.value)}
                                />
                            </div>
                            <div className="mb-3">
                                <label className="form-label">Password</label>
                                <input
                                    type="password"
                                    className="form-control"
                                    value={password}
                                    onChange={(e) => setPassword(e.target.value)}
                                />
                            </div>
                            <button type="submit" className="btn btn-primary w-100">
                                Login
                            </button>
                            {error && <div className="alert alert-danger mt-3">{error}</div>}
                        </form>
                    </div>
                </div>
            </div>
        </div>
    );
};

export default Login;


3- SERVICE - api.ts

import axios from 'axios';

const api = axios.create({
    baseURL: 'http://localhost:9091/api', // replace with your backend base URL
});

// Optional: automatically attach token if available
api.interceptors.request.use((config) => {
    const token = localStorage.getItem('authToken');
    if (token) {
        console.log(token);
        config.headers['authorization'] = token;
    }
    return config;
});

export default api;


4- PROTECTION - PrivateRoute.tsx

import React from 'react';
import { Navigate } from 'react-router-dom';

interface Props {
    allowedRoles?: string[]; // roles like ['1', '2']
    children: React.ReactNode;
}

// Protects routes by checking for auth token
const PrivateRoute: React.FC<Props> = ({allowedRoles, children }) => {
   
    const token = localStorage.getItem('authToken');
    const roleId = localStorage.getItem('roleId');

    if (!token) {
        return <Navigate to="/login" replace />;
    }
   
    if (allowedRoles && !allowedRoles.includes(roleId || '')) {
        return <Navigate to="/unauthorized" replace />;
    }
   
    return <>{children}</>;
};

export default PrivateRoute;


5- APPLICATION - App.tsx

 import React, { useState } from "react";

import { BrowserRouter as Router, Routes, Route, Navigate, useLocation, useNavigate } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "@fortawesome/fontawesome-free/css/all.min.css";

// Component imports
import AddEmployee from "./components/AddEnployee";
import EmployeeList from "./components/EmployeeList";
import EditEmployee from "./components/EditEmployee";
import NaviBar from "./components/NaviBar";
import Login from "./components/Login";
import PrivateRoute from "./components/PrivateRoute";
import Unauthorized from "./components/Unauthorized";
import AdminDashboard from './components/AdminDashBoard';
import ManagerDashboard from './components/ManagerDashboard';
import ReceptionistDashboard from './components/ReceptionistDashboard';

// Main application content component
const AppContent: React.FC = () => {
  // State management for user authentication
  const [userName, setUserName] = useState<string>(
    // Initialize userName from localStorage or empty string
    () => localStorage.getItem("userName") || ""
  );
  
  // Assume user is logged in initially (might need adjustment)
  const [isLoggedIn, setIsLoggedIn] = useState<boolean>(true);
  
  // Router hooks
  const location = useLocation(); // Gets current route location
  const showHeader = location.pathname !== "/login"; // Hide header on login page
  const navigate = useNavigate(); // Programmatic navigation

  // Logout handler function
  const handleLogout = () => {
    localStorage.clear();  // Clear all client-side storage
    setIsLoggedIn(false); // Update auth state
    setUserName("");      // Clear username
    navigate("/login");   // Redirect to login
  };

  return (
    <>
      {/* Conditional header rendering */}
      {showHeader && <NaviBar userName={userName} onLogout={handleLogout} />}
      
      {/* Main content container */}
      <div className="container mt-4">
        {/* Route configuration */}
        <Routes>
          {/* Public routes */}

          {/* Login route with success callback */}
          <Route 
            path="/login" 
            element={
              <Login onLoginSuccess={(username) => {
                setUserName(username);
                setIsLoggedIn(true);
                navigate('/list'); // Redirect after login
              }} 
            />
          } 
          />
          
          {/* Unauthorized access route */}
          <Route path="/unauthorized" element={<Unauthorized />} />

          {/* Role-protected routes */}

          {/* Admin route (role 1) */}
          <Route
            path="/admin"
            element={
              <PrivateRoute allowedRoles={['1']}>
                <AdminDashboard />
              </PrivateRoute>
            }
          />
          
          {/* Manager route (role 2) */}
          <Route
            path="/manager"
            element={
              <PrivateRoute allowedRoles={['2']}>
                <ManagerDashboard />
              </PrivateRoute>
            }
          />
          
          {/* Receptionist route (role 3) */}
          <Route
            path="/receptionist"
            element={
              <PrivateRoute allowedRoles={['3']}>
                <ReceptionistDashboard />
              </PrivateRoute>
            }
          />

          {/* Employee list route (protected but no specific role required) */}
          <Route
            path="/list"
            element={
              <PrivateRoute>
                <EmployeeList />
              </PrivateRoute>
            }
          />
          
          {/* Add employee route */}
          <Route
            path="/add"
            element={
              <PrivateRoute>
                <AddEmployee />
              </PrivateRoute>
            }
          />
          
          {/* Edit employee route (dynamic parameter) */}
          <Route
            path="/edit/:empId"
            element={
              <PrivateRoute>
                <EditEmployee />
              </PrivateRoute>
            }
          />

          {/* Catch-all route for unknown paths */}
          <Route path="*" element={<Navigate to="/unauthorized" replace />} />
        </Routes>
      </div>
    </>
  );
};

// Main App component with Router wrapper
const App: React.FC = () => (
  <Router>
    <AppContent />
  </Router>
);

export default App;


Key Architecture and Decisions:


  1. Authentication Flow:

    • Uses localStorage for persistent user session

    • Implements login/logout state management

    • Assumes initial logged-in state (may need adjustment)


  2. Routing Structure:

    • Public routes (login, unauthorized)

    • Role-protected routes (admin, manager, receptionist)

    • General protected routes (employee list, add/edit)

    • 404-style catch-all route


  3. Security:

    • PrivateRoute component guards protected routes

    • Role-based access control via allowedRoles prop

    • Automatic cleanup on logout (localStorage.clear())


  4. UI Decisions:

    • Conditional header rendering

    • Bootstrap styling framework

    • Font Awesome icons

    • Consistent container layout


  5. Navigation:

    • Programmatic navigation after login/logout

    • Route parameters for dynamic paths (edit/:empId)

    • Replace navigation to prevent back-button issues

No comments:

Post a Comment