Dealer_Onboard_Frontend/src/App.tsx

293 lines
16 KiB
TypeScript

import { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { RootState } from './store';
import { setCredentials, logout as logoutAction, initializeAuth } from './store/slices/authSlice';
import { RoleGuard } from './components/auth/RoleGuard';
import { Routes, Route, Navigate, useLocation, useNavigate, Outlet } from 'react-router-dom';
import { ApplicationFormPage } from './components/public/ApplicationFormPage';
import PublicQuestionnairePage from './pages/public/PublicQuestionnairePage';
import { LoginPage } from './components/auth/LoginPage';
import { ProspectiveLoginPage } from './components/auth/ProspectiveLoginPage';
import { Sidebar } from './components/layout/Sidebar';
import { Header } from './components/layout/Header';
import { Dashboard } from './components/dashboard/Dashboard';
import { FinanceDashboard } from './components/dashboard/FinanceDashboard';
import { DealerDashboard } from './components/dashboard/DealerDashboard';
import { ProspectiveDashboardPage } from './components/dashboard/ProspectiveDashboardPage';
import { ApplicationsPage } from './components/applications/ApplicationsPage';
import { AllApplicationsPage } from './components/applications/AllApplicationsPage';
import { OpportunityRequestsPage } from './components/applications/OpportunityRequestsPage';
import { UnopportunityRequestsPage } from './components/applications/UnopportunityRequestsPage';
import { ApplicationDetails } from './components/applications/ApplicationDetails';
import { ResignationPage } from './components/applications/ResignationPage';
import { TerminationPage } from './components/applications/TerminationPage';
import { FnFPage } from './components/applications/FnFPage';
import { ResignationDetails } from './components/applications/ResignationDetails';
import { TerminationDetails } from './components/applications/TerminationDetails';
import { FnFDetails } from './components/applications/FnFDetails';
import { FinanceOnboardingPage } from './components/applications/FinanceOnboardingPage';
import { FinanceFnFPage } from './components/applications/FinanceFnFPage';
import { FinancePaymentDetailsPage } from './components/applications/FinancePaymentDetailsPage';
import { FinanceFnFDetailsPage } from './components/applications/FinanceFnFDetailsPage';
import { MasterPage } from './components/applications/MasterPage';
import { UserManagementPage } from './components/admin/UserManagementPage';
import { ConstitutionalChangePage } from './components/applications/ConstitutionalChangePage';
import { ConstitutionalChangeDetails } from './components/applications/ConstitutionalChangeDetails';
import { RelocationRequestPage } from './components/applications/RelocationRequestPage';
import { RelocationRequestDetails } from './components/applications/RelocationRequestDetails';
import { WorknotePage } from './components/applications/WorknotePage';
import { DealerResignationPage } from './components/dealer/DealerResignationPage';
import { DealerConstitutionalChangePage } from './components/dealer/DealerConstitutionalChangePage';
import { DealerRelocationPage } from './components/dealer/DealerRelocationPage';
import QuestionnaireBuilder from './components/admin/QuestionnaireBuilder';
import QuestionnaireList from './components/admin/QuestionnaireList';
import { Toaster } from './components/ui/sonner';
import { User } from './lib/mock-data';
import { toast } from 'sonner';
import { API } from './api/API';
// Layout Component
const AppLayout = ({ onLogout, title }: { onLogout: () => void, title: string }) => {
return (
<div className="flex h-screen bg-slate-50">
<Sidebar onLogout={onLogout} />
<div className="flex-1 flex flex-col overflow-hidden">
<Header title={title} onRefresh={() => window.location.reload()} />
<main className="flex-1 overflow-y-auto p-6">
<Outlet />
</main>
</div>
<Toaster />
</div>
);
};
export default function App() {
const dispatch = useDispatch<any>();
const { user: currentUser, isAuthenticated, loading } = useSelector((state: RootState) => state.auth);
const [showAdminLogin, setShowAdminLogin] = useState(false);
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
dispatch(initializeAuth());
}, [dispatch]);
const handleLogin = async (email: string, password: string) => {
try {
const response = await API.login({ email, password });
if (response.ok && response.data) {
const { token, user } = response.data as any;
localStorage.setItem('token', token);
const simplifiedUser: User = {
id: user.id,
name: user.fullName || email.split('@')[0],
email: user.email,
password: password, // Note: storing password in state is not ideal, but keeping existing structure
role: typeof user.role === 'string' ? user.role : (user.roleCode || 'User')
};
dispatch(setCredentials({ user: simplifiedUser, token }));
toast.success(`Welcome back, ${simplifiedUser.name}!`);
setShowAdminLogin(false);
} else {
const errorMsg = (response.data as any)?.message || 'Invalid credentials';
toast.error(errorMsg);
}
} catch (error) {
console.error('Login error:', error);
toast.error('Something went wrong. Please try again.');
}
};
const handleLogout = () => {
dispatch(logoutAction());
setShowAdminLogin(false);
toast.info('Logged out successfully');
navigate('/');
};
// Listen for 401 logout events from API client
useEffect(() => {
const onLogout = () => {
handleLogout();
toast.error('Session expired. Please login again.');
};
window.addEventListener('auth:logout', onLogout);
return () => window.removeEventListener('auth:logout', onLogout);
}, [dispatch, navigate]);
// Helper to determine page title based on path
const getPageTitle = (pathname: string) => {
if (pathname.startsWith('/applications/') && pathname.length > 14) return 'Application Details';
if (pathname.includes('/resignation/') && pathname.length > 13) return 'Resignation Details';
// ... Add more dynamic title logic as needed
const titles: Record<string, string> = {
'/dashboard': 'Dashboard',
'/applications': 'Dealership Requests',
'/all-applications': 'All Applications',
'/opportunity-requests': 'Opportunity Requests',
'/unopportunity-requests': 'Unopportunity Requests',
'/tasks': 'My Tasks',
'/reports': 'Reports & Analytics',
'/settings': 'Settings',
'/users': 'User Management',
'/resignation': 'Resignation Management',
'/termination': 'Termination Management',
'/fnf': 'Full & Final Settlement',
'/finance-onboarding': 'Payment Verification',
'/finance-fnf': 'F&F Financial Settlement',
'/master': 'Master Configuration',
'/constitutional-change': 'Constitutional Change',
'/relocation-requests': 'Relocation Requests',
'/dealer-resignation': 'Dealer Resignation Management',
'/dealer-constitutional': 'Dealer Constitutional Change',
'/dealer-relocation': 'Dealer Relocation Requests',
'/questionnaire-builder': 'Questionnaire Builder',
};
return titles[pathname] || 'Dashboard';
};
if (loading) {
return (
<div className="flex items-center justify-center h-screen bg-slate-50">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-red-600"></div>
</div>
);
}
// Public Routes
if (!isAuthenticated) {
return (
<>
<Routes>
<Route path="/admin-login" element={<LoginPage onLogin={handleLogin} />} />
<Route path="/prospective-login" element={<ProspectiveLoginPage />} />
<Route path="/questionnaire/:applicationId" element={<PublicQuestionnairePage />} />
<Route path="*" element={showAdminLogin ? <LoginPage onLogin={handleLogin} /> :
<ApplicationFormPage onAdminLogin={() => setShowAdminLogin(true)} />}
/>
</Routes>
<Toaster />
</>
)
}
// Protected Routes
return (
<Routes>
{/* Prospective Dealer Route - STRICTLY ISOLATED */}
<Route
path="/prospective-dashboard"
element={
<RoleGuard allowedRoles={['Prospective Dealer']}>
<ProspectiveDashboardPage />
</RoleGuard>
}
/>
{/* Internal & Dealer Routes - EXCLUDES Prospective Dealers */}
<Route element={
<RoleGuard excludeRoles={['Prospective Dealer']} redirectTo="/prospective-dashboard">
<AppLayout onLogout={handleLogout} title={getPageTitle(location.pathname)} />
</RoleGuard>
}>
<Route path="/" element={<Navigate to="/dashboard" replace />} />
{/* Dashboards */}
<Route path="/dashboard" element={
currentUser?.role === 'Finance Admin' || currentUser?.role === 'Finance' ?
<FinanceDashboard currentUser={currentUser} onNavigate={(path) => navigate(`/${path}`)} onViewPaymentDetails={(id) => navigate(`/finance-onboarding/${id}`)} onViewFnFDetails={(id) => navigate(`/finance-fnf/${id}`)} /> :
currentUser?.role === 'Dealer' ?
<DealerDashboard currentUser={currentUser} onNavigate={(path) => navigate(`/${path}`)} /> :
<Dashboard onNavigate={(path) => navigate(`/${path}`)} />
} />
{/* Applications */}
<Route path="/applications" element={<ApplicationsPage onViewDetails={(id) => navigate(`/applications/${id}`)} initialFilter="all" />} />
<Route path="/applications/:id" element={<ApplicationDetails applicationId={window.location.pathname.split('/').pop() || ''} onBack={() => navigate('/applications')} />} />
<Route path="/all-applications" element={
currentUser?.role === 'DD' ? <AllApplicationsPage onViewDetails={(id) => navigate(`/applications/${id}`)} initialFilter="all" /> : <Navigate to="/dashboard" />
} />
{/* Admin/Lead Routes */}
<Route path="/opportunity-requests" element={<OpportunityRequestsPage onViewDetails={(id) => navigate(`/applications/${id}`)} />} />
<Route path="/unopportunity-requests" element={<UnopportunityRequestsPage onViewDetails={(id) => navigate(`/applications/${id}`)} />} />
{/* Other Modules */}
<Route path="/users" element={<UserManagementPage />} />
<Route path="/master" element={<MasterPage />} />
<Route path="/questions" element={<QuestionnaireList />} />
<Route path="/questionnaire-builder" element={<QuestionnaireBuilder />} />
<Route path="/questionnaire-builder/:id" element={<QuestionnaireBuilder />} />
<Route path="/questionnaires" element={<QuestionnaireList />} />
{/* HR/Finance Modules (Simplified for brevity, following pattern) */}
<Route path="/resignation" element={<ResignationPage currentUser={currentUser} onViewDetails={(id) => navigate(`/resignation/${id}`)} />} />
<Route path="/resignation/:id" element={<ResignationDetails resignationId={window.location.pathname.split('/').pop() || ''} onBack={() => navigate('/resignation')} currentUser={currentUser} />} />
<Route path="/termination" element={<TerminationPage currentUser={currentUser} onViewDetails={(id) => navigate(`/termination/${id}`)} />} />
<Route path="/termination/:id" element={<TerminationDetails terminationId={window.location.pathname.split('/').pop() || ''} onBack={() => navigate('/termination')} currentUser={currentUser} />} />
<Route path="/fnf" element={<FnFPage currentUser={currentUser} onViewDetails={(id) => navigate(`/fnf/${id}`)} />} />
<Route path="/fnf/:id" element={<FnFDetails fnfId={window.location.pathname.split('/').pop() || ''} onBack={() => navigate('/fnf')} currentUser={currentUser} />} />
<Route path="/finance-onboarding" element={<FinanceOnboardingPage onViewPaymentDetails={(id) => navigate(`/finance-onboarding/${id}`)} />} />
<Route path="/finance-onboarding/:id" element={<FinancePaymentDetailsPage applicationId={window.location.pathname.split('/').pop() || ''} onBack={() => navigate('/finance-onboarding')} />} />
<Route path="/finance-fnf" element={<FinanceFnFPage onViewFnFDetails={(id) => navigate(`/finance-fnf/${id}`)} />} />
<Route path="/finance-fnf/:id" element={<FinanceFnFDetailsPage fnfId={window.location.pathname.split('/').pop() || ''} onBack={() => navigate('/finance-fnf')} />} />
<Route path="/constitutional-change" element={<ConstitutionalChangePage currentUser={currentUser} onViewDetails={(id) => navigate(`/constitutional-change/${id}`)} />} />
<Route path="/constitutional-change/:id" element={<ConstitutionalChangeDetails requestId={window.location.pathname.split('/').pop() || ''} onBack={() => navigate('/constitutional-change')} currentUser={currentUser} onOpenWorknote={() => { }} />} />
<Route path="/relocation-requests" element={<RelocationRequestPage currentUser={currentUser} onViewDetails={(id) => navigate(`/relocation-requests/${id}`)} />} />
<Route path="/relocation-requests/:id" element={<RelocationRequestDetails requestId={window.location.pathname.split('/').pop() || ''} onBack={() => navigate('/relocation-requests')} currentUser={currentUser} onOpenWorknote={() => { }} />} />
{/* Dealer Routes */}
<Route path="/dealer-resignation" element={<DealerResignationPage currentUser={currentUser} onViewDetails={(id) => navigate(`/resignation/${id}`)} />} />
<Route path="/dealer-constitutional" element={<DealerConstitutionalChangePage currentUser={currentUser} onViewDetails={(id) => navigate(`/constitutional-change/${id}`)} />} />
<Route path="/dealer-relocation" element={<DealerRelocationPage currentUser={currentUser} onViewDetails={(id) => navigate(`/relocation-requests/${id}`)} />} />
{/* Placeholder Routes */}
<Route path="/tasks" element={
<div className="bg-white rounded-lg border border-slate-200 p-8 text-center">
<h2 className="text-slate-900 mb-2">My Tasks</h2>
<p className="text-slate-600">Task management interface would be displayed here</p>
<p className="text-slate-500 mt-4">Shows applications assigned to the current user</p>
</div>
} />
<Route path="/reports" element={
<div className="bg-white rounded-lg border border-slate-200 p-8 text-center">
<h2 className="text-slate-900 mb-2">Reports & Analytics</h2>
<p className="text-slate-600">Advanced reporting and analytics dashboard</p>
<p className="text-slate-500 mt-4">Charts, export capabilities, and custom filters</p>
</div>
} />
<Route path="/settings" element={
<div className="bg-white rounded-lg border border-slate-200 p-8">
<h2 className="text-slate-900 mb-4">Settings</h2>
<div className="space-y-4">
<div>
<h3 className="text-slate-900 mb-2">Profile Settings</h3>
<p className="text-slate-600">Update your profile information and preferences</p>
</div>
<div>
<h3 className="text-slate-900 mb-2">Notification Preferences</h3>
<p className="text-slate-600">Configure email and system notifications</p>
</div>
<div>
<h3 className="text-slate-900 mb-2">Security</h3>
<p className="text-slate-600">Change password and manage security settings</p>
</div>
</div>
</div>
} />
{/* Fallback */}
<Route path="*" element={<Navigate to="/dashboard" replace />} />
</Route>
</Routes>
);
}