fcsc_ipi_frontend/ipi-survey-platform/src/App.jsx
2026-01-23 14:22:28 +05:30

403 lines
12 KiB
JavaScript

import React, { useEffect, useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { requestOtp } from '@/services/auth/authService';
import { Routes, Route, Navigate } from 'react-router-dom';
import Dashboard from '@/pages/Dashboard/Dashboard';
import AdminDashboard from '@/pages/Admin/AdminDashboard';
import AdminUsers from '@/pages/Admin/AdminUsers';
import Validations from '@/pages/Admin/Validations';
import ValidationReview from '@/pages/Admin/ValidationReview';
import Configuration from '@/pages/Admin/Configuration';
import Survey from '@/pages/Survey/Survey';
import Overview from '@/pages/Overview/Overview';
import Login from '@/pages/Login/Login';
import OTPVerification from '@/pages/OTPVerification/OTPVerification';
import History from '@/pages/History/History';
import ChangePassword from '@/pages/ChangePassword/ChangePassword';
import ForgotPassword from '@/pages/ForgotPassword/ForgotPassword';
import PublicContactForm from '@/pages/PublicContactForm';
import EditCompanyProfile from '@/pages/Admin/configuration/EditCompanyProfile';
import Navbar from './pages/LandingPage/component/Navbar';
import ManufacturingIndex from '@/pages/ManufacturingIndex/ManufacturingIndex';
const RequireOTPVerification = ({ children }) => {
const [isVerified, setIsVerified] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
const checkVerification = async () => {
try {
const userProfile = JSON.parse(localStorage.getItem('user_profile') || '{}');
// Check if user is logged in and has a valid role
const role = localStorage.getItem('user_role');
const allowedRoles = ['EstablishmentUser', 'Admin'];
if (!role || !allowedRoles.includes(role)) {
navigate('/index', { replace: true });
return;
}
// Check if user is OTP verified
if (userProfile.isOtpVerified) {
setIsVerified(true);
} else {
// If not verified, redirect to index page
navigate('/index', { replace: true });
}
} catch (error) {
console.error('Verification check failed:', error);
navigate('/index', { replace: true });
} finally {
setIsLoading(false);
}
};
checkVerification();
}, [navigate]);
if (isLoading) {
return <div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary"></div>
</div>;
}
return isVerified ? children : null;
};
const RequireRole = ({ allowedRoles = [], children }) => {
let role;
try {
role = localStorage.getItem("user_role");
} catch {
role = null;
}
const normalizedRole = String(role || "").toLowerCase();
const isAllowed = allowedRoles.some(
(allowed) => String(allowed || "").toLowerCase() === normalizedRole
);
if (isAllowed) {
return children;
}
return <Navigate to="/index" replace />;
};
const SessionWarningModal = ({ show, remainingTime, onExtend, onLogout }) => {
if (!show) return null;
const minutes = Math.floor(remainingTime / 60);
const seconds = remainingTime % 60;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
<div className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 p-6">
<div className="flex items-start gap-4">
<div className="flex-shrink-0 w-12 h-12 rounded-full bg-yellow-100 flex items-center justify-center">
<svg className="w-6 h-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold text-gray-900 mb-2">
Session Expiring Soon
</h3>
<p className="text-sm text-gray-600 mb-4">
Your session will expire in <strong className="text-yellow-600 font-mono text-base">{minutes}:{seconds.toString().padStart(2, '0')}</strong> due to inactivity.
</p>
<p className="text-sm text-gray-600 mb-6">
Would you like to stay signed in?
</p>
<div className="flex gap-3">
<button
onClick={onExtend}
className="flex-1 bg-[#92722A] hover:bg-[#7b5c1f] text-white px-4 py-2 rounded-lg font-medium transition-colors"
>
Stay Signed In
</button>
<button
onClick={onLogout}
className="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg font-medium transition-colors"
>
Log Out Now
</button>
</div>
</div>
</div>
</div>
</div>
);
};
function App() {
const [showWarning, setShowWarning] = React.useState(false);
const [warningTime, setWarningTime] = React.useState(0);
React.useEffect(() => {
const TIMEOUT_MINUTES = 15;
const WARNING_MINUTES = 2;
let timer;
let warningTimer;
let warningInterval;
const clearAllTimers = () => {
clearTimeout(timer);
clearTimeout(warningTimer);
clearInterval(warningInterval);
timer = null;
warningTimer = null;
warningInterval = null;
};
const performLogout = () => {
sessionStorage.clear();
localStorage.removeItem('failed_attempts');
localStorage.removeItem('lock_until');
setShowWarning(false);
alert('Session expired. Please log in again.');
window.location.href = '/login';
};
const showWarningModal = () => {
setShowWarning(true);
let timeLeft = WARNING_MINUTES * 60;
setWarningTime(timeLeft);
warningInterval = setInterval(() => {
timeLeft--;
setWarningTime(timeLeft);
if (timeLeft <= 0) {
clearInterval(warningInterval);
performLogout();
}
}, 1000);
};
const resetTimer = () => {
clearAllTimers();
setShowWarning(false);
warningTimer = setTimeout(() => {
showWarningModal();
}, (TIMEOUT_MINUTES - WARNING_MINUTES) * 60 * 1000);
timer = setTimeout(() => {
performLogout();
}, TIMEOUT_MINUTES * 60 * 1000);
};
const handleExtendSession = () => {
setShowWarning(false);
resetTimer();
};
const handleLogoutNow = () => {
clearAllTimers();
performLogout();
};
window.handleExtendSession = handleExtendSession;
window.handleLogoutNow = handleLogoutNow;
const events = ['mousemove', 'keydown', 'click', 'scroll', 'touchstart'];
events.forEach(event => {
window.addEventListener(event, resetTimer);
});
resetTimer();
return () => {
clearAllTimers();
events.forEach(event => {
window.removeEventListener(event, resetTimer);
});
delete window.handleExtendSession;
delete window.handleLogoutNow;
};
}, []);
return (
<>
<SessionWarningModal
show={showWarning}
remainingTime={warningTime}
onExtend={() => window.handleExtendSession?.()}
onLogout={() => window.handleLogoutNow?.()}
/>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/verify-otp"
element={
<RequireRole allowedRoles={['EstablishmentUser', 'Admin']}>
<OTPVerification />
</RequireRole>
}
/>
<Route
path="/history"
element={
<RequireRole allowedRoles={['EstablishmentUser']}>
<History />
</RequireRole>
}
/>
<Route
path="/dashboard"
element={
<RequireOTPVerification>
<RequireRole allowedRoles={['EstablishmentUser']}>
<Dashboard />
</RequireRole>
</RequireOTPVerification>
}
/>
<Route
path="/admin/dashboard"
element={
<RequireOTPVerification>
<RequireRole allowedRoles={['Admin']}>
<AdminDashboard />
</RequireRole>
</RequireOTPVerification>
}
/>
<Route
path="/admin/validations"
element={
<RequireOTPVerification>
<RequireRole allowedRoles={['Admin']}>
<Validations />
</RequireRole>
</RequireOTPVerification>
}
/>
<Route
path="/admin/validations/:id"
element={
<RequireOTPVerification>
<RequireRole allowedRoles={['Admin']}>
<ValidationReview />
</RequireRole>
</RequireOTPVerification>
}
/>
{/* <Route path="/admin/configuration">
<Route
index
element={
<RequireRole allowedRoles={['Admin', 'EstablishmentUser']}>
<Configuration />
</RequireRole>
}
/>
</Route> */}
<Route
path="/admin/configuration/*"
element={
<RequireOTPVerification>
<RequireRole allowedRoles={['Admin', 'EstablishmentUser']}>
<Configuration />
</RequireRole>
</RequireOTPVerification>
}
/>
{/* /> */}
{/* </Route> */}
<Route
path="/manufacturing-index"
element={
<RequireOTPVerification>
<RequireRole allowedRoles={['Admin', 'EstablishmentUser']}>
<ManufacturingIndex />
</RequireRole>
</RequireOTPVerification>
}
/>
<Route
path="/admin/users"
element={
<RequireOTPVerification>
<RequireRole allowedRoles={['Admin']}>
<AdminUsers />
</RequireRole>
</RequireOTPVerification>
}
/>
<Route path="/" element={<Navigate to="/dashboard" replace />} />
<Route
path="/survey"
element={
<RequireOTPVerification>
<RequireRole allowedRoles={['EstablishmentUser']}>
<Survey />
</RequireRole>
</RequireOTPVerification>
}
/>
<Route
path="/overview"
element={
<RequireOTPVerification>
<RequireRole allowedRoles={['EstablishmentUser']}>
<Overview />
</RequireRole>
</RequireOTPVerification>
}
/>
{/* <Route path="profile">
<Route
index
element={
<RequireRole allowedRoles={['EstablishmentUser']}>
<EditCompanyProfile />
</RequireRole>
}
/>
</Route> */}
{/* <Route path="/profile/edit/:id" element={
<RequireRole allowedRoles={['EstablishmentUser']}>
<EditCompanyProfile />
</RequireRole>
} /> */}
{/* <Route
path="/admin/configuration/profile/:id?"
element={
<RequireRole allowedRoles={['EstablishmentUser']}>
<EditCompanyProfile />
</RequireRole>
}
/> */}
<Route
path="/edit-profile/:id"
element={
<RequireOTPVerification>
<RequireRole allowedRoles={['EstablishmentUser']}>
<EditCompanyProfile />
</RequireRole>
</RequireOTPVerification>
}
/>
<Route path="/change-password" element={<ChangePassword />} />
<Route path="/forgot-password" element={<ForgotPassword />} />
<Route path="/reset-password" element={<PublicContactForm />} />
<Route path="*" element={<Navigate to="/" replace />} />
{/* landingpage */}
<Route path="/index" element={<Navbar />} />
</Routes>
</>
);
}
export default App;