implement TeleEMS platform architecture with centralized API client and master data management system
This commit is contained in:
244
src/App.tsx
Normal file
244
src/App.tsx
Normal file
@@ -0,0 +1,244 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { BrowserRouter, Routes, Route, useLocation, Navigate } from 'react-router-dom';
|
||||
import { Sidebar } from './components/Sidebar';
|
||||
import { TopBar } from './components/TopBar';
|
||||
import { ErrorBoundary } from './components/ErrorBoundary';
|
||||
import { Dashboard } from './pages/Dashboard';
|
||||
import { LiveIncidents } from './pages/LiveIncidents';
|
||||
import { FleetDispatch } from './pages/FleetDispatch';
|
||||
import { PatientClinical } from './pages/PatientClinical';
|
||||
import { HospitalsNetwork } from './pages/HospitalsNetwork';
|
||||
import { AnalyticsReports } from './pages/AnalyticsReports';
|
||||
import { UserManagement } from './pages/UserManagement';
|
||||
import { PlatformConfig } from './pages/PlatformConfig';
|
||||
import { AuditCompliance } from './pages/AuditCompliance';
|
||||
import { SystemHealth } from './pages/SystemHealth';
|
||||
import { HospitalConsole } from './pages/HospitalConsole';
|
||||
import { MasterDataManagement } from './pages/MasterData';
|
||||
import { CallerPortal } from './pages/CallerPortal';
|
||||
import { Login } from './pages/Login';
|
||||
import { FleetLogin } from './pages/FleetLogin';
|
||||
import { FleetOperatorDashboard } from './pages/FleetOperatorDashboard';
|
||||
import { PerspectiveLauncher } from './pages/PerspectiveLauncher';
|
||||
import { RoleLogin } from './pages/RoleLogin';
|
||||
import { ComingSoonPortal } from './pages/ComingSoonPortal';
|
||||
import {
|
||||
Building2,
|
||||
Stethoscope,
|
||||
Activity,
|
||||
User,
|
||||
Scan,
|
||||
ShoppingCart
|
||||
} from 'lucide-react';
|
||||
|
||||
import { isTokenExpired, logout } from './utils/auth';
|
||||
|
||||
// --- ROLE-BASED ACCESS CONTROL ---
|
||||
const RoleProtectedRoute: React.FC<{
|
||||
children: React.ReactNode,
|
||||
allowedRoles: string[],
|
||||
user: any
|
||||
}> = ({ children, allowedRoles, user }) => {
|
||||
const isAuthenticated = localStorage.getItem('teleems_auth') === 'true';
|
||||
|
||||
if (!isAuthenticated) return <Navigate to="/login" replace />;
|
||||
|
||||
const userRoles = Array.isArray(user?.roles) ? user.roles : [];
|
||||
const hasAccess = allowedRoles.some(role => userRoles.includes(role)) || userRoles.includes('CURESELECT_ADMIN');
|
||||
|
||||
if (!hasAccess) {
|
||||
// Redirect to their respective "home" if they don't have access
|
||||
if (userRoles.includes('FLEET_OPERATOR')) return <Navigate to="/fleet-operator" replace />;
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
function AppContent() {
|
||||
const location = useLocation();
|
||||
|
||||
// --- SESSION MONITORING ---
|
||||
// Periodically check if the token has expired
|
||||
useEffect(() => {
|
||||
const checkSession = () => {
|
||||
const token = localStorage.getItem('teleems_token');
|
||||
const auth = localStorage.getItem('teleems_auth') === 'true';
|
||||
|
||||
if (auth && token && isTokenExpired(token)) {
|
||||
console.warn('Session expired. Logging out...');
|
||||
logout();
|
||||
}
|
||||
};
|
||||
|
||||
// Check on mount
|
||||
checkSession();
|
||||
|
||||
// Check on every route change
|
||||
checkSession();
|
||||
|
||||
// Periodically check every 30 seconds
|
||||
const interval = setInterval(checkSession, 30000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [location.pathname]);
|
||||
|
||||
// --- DEVELOPMENT BYPASS ---
|
||||
// In a real production app, this would be removed.
|
||||
// For the user's request: "this admin so don't want login give me admin level access"
|
||||
/* Commented out to allow testing of launcher and login flow
|
||||
useEffect(() => {
|
||||
const isAuth = localStorage.getItem('teleems_auth') === 'true';
|
||||
const isCaller = window.location.pathname === '/caller';
|
||||
const isLogin = window.location.pathname === '/login';
|
||||
|
||||
if (!isAuth && !isCaller && !isLogin) {
|
||||
console.log('Dev Mode: Auto-authenticating as CureSelect Super Admin');
|
||||
localStorage.setItem('teleems_auth', 'true');
|
||||
localStorage.setItem('teleems_token', 'dev-super-token-2026');
|
||||
localStorage.setItem('teleems_user', JSON.stringify({
|
||||
id: 'admin-001',
|
||||
username: 'CureSelect Super Admin',
|
||||
roles: ['CURESELECT_ADMIN', 'ADMIN'],
|
||||
metadata: {
|
||||
organization: { company_name: 'CureSelect Healthcare LLP' }
|
||||
}
|
||||
}));
|
||||
// Force reload to update navigation
|
||||
window.location.reload();
|
||||
}
|
||||
}, []);
|
||||
*/
|
||||
|
||||
const isLoginPage = location.pathname.startsWith('/login') || location.pathname === '/fleet-login' || location.pathname === '/launcher';
|
||||
|
||||
const isAuthenticated = localStorage.getItem('teleems_auth') === 'true';
|
||||
const user = JSON.parse(localStorage.getItem('teleems_user') || '{}');
|
||||
|
||||
// --- PUBLIC ROUTES (No Auth Required) ---
|
||||
if (isLoginPage || (location.pathname === '/' && !isAuthenticated)) {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<PerspectiveLauncher />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/login/:role" element={<RoleLogin />} />
|
||||
<Route path="/fleet-login" element={<FleetLogin />} />
|
||||
<Route path="/launcher" element={<PerspectiveLauncher />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
// --- PROTECTED ROUTES (Auth Required) ---
|
||||
if (!isAuthenticated) {
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="dashboard-container">
|
||||
<ErrorBoundary>
|
||||
<Sidebar />
|
||||
</ErrorBoundary>
|
||||
<main className="main-content">
|
||||
<div className="scanline" />
|
||||
<TopBar />
|
||||
<div style={{ flex: 1, overflow: 'hidden', position: 'relative', display: 'flex', flexDirection: 'column' }}>
|
||||
<ErrorBoundary>
|
||||
<Routes>
|
||||
<Route path="/" element={
|
||||
isAuthenticated ? (
|
||||
user?.roles?.includes('FLEET_OPERATOR') && !user?.roles?.includes('CURESELECT_ADMIN')
|
||||
? <Navigate to="/fleet-operator" replace />
|
||||
: <Dashboard />
|
||||
) : (
|
||||
<Navigate to="/launcher" replace />
|
||||
)
|
||||
} />
|
||||
<Route path="/incidents" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN', 'PILOT', 'COORDINATOR']} user={user}>
|
||||
<LiveIncidents />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/fleet" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN', 'STATION_INCHARGE']} user={user}>
|
||||
<FleetDispatch />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/clinical" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN', 'HOSPITAL_ADMIN', 'ED_DOCTOR', 'EMT']} user={user}>
|
||||
<PatientClinical />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/hospitals" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN']} user={user}>
|
||||
<HospitalsNetwork />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/analytics" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN', 'HOSPITAL_ADMIN', 'STATION_INCHARGE']} user={user}>
|
||||
<AnalyticsReports />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/users" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN']} user={user}>
|
||||
<UserManagement />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/config" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN']} user={user}>
|
||||
<PlatformConfig />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/compliance" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN', 'HOSPITAL_ADMIN']} user={user}>
|
||||
<AuditCompliance />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/health" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN']} user={user}>
|
||||
<SystemHealth />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/hospital-console" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN', 'HOSPITAL_ADMIN', 'ED_DOCTOR', 'COORDINATOR', 'EMT']} user={user}>
|
||||
<HospitalConsole />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/master-data" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN']} user={user}>
|
||||
<MasterDataManagement />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
<Route path="/caller" element={<CallerPortal />} />
|
||||
<Route path="/fleet-operator" element={
|
||||
<RoleProtectedRoute allowedRoles={['CURESELECT_ADMIN', 'FLEET_OPERATOR']} user={user}>
|
||||
<FleetOperatorDashboard />
|
||||
</RoleProtectedRoute>
|
||||
} />
|
||||
|
||||
{/* --- NEW PERSPECTIVE PORTALS --- */}
|
||||
<Route path="/launcher" element={<PerspectiveLauncher />} />
|
||||
<Route path="/hospital-group" element={<ComingSoonPortal title="Hospital Group" icon={Building2} />} />
|
||||
<Route path="/provider" element={<ComingSoonPortal title="Provider" icon={Stethoscope} />} />
|
||||
<Route path="/provider-react" element={<ComingSoonPortal title="Provider React" icon={Activity} />} />
|
||||
<Route path="/patient-portal" element={<ComingSoonPortal title="Patient" icon={User} />} />
|
||||
<Route path="/scan-centre" element={<ComingSoonPortal title="Scan Centre" icon={Scan} />} />
|
||||
<Route path="/cart" element={<ComingSoonPortal title="Cart / Mobile" icon={ShoppingCart} />} />
|
||||
|
||||
</Routes>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<AppContent />
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user