251 lines
12 KiB
TypeScript
251 lines
12 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import {
|
|
FileText,
|
|
LogOut,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
User,
|
|
RefreshCw,
|
|
} from 'lucide-react';
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
import { useNavigate, Routes, Route, useParams, useLocation } from 'react-router-dom';
|
|
import { RootState } from '@/store';
|
|
import { logout } from '@/store/slices/authSlice';
|
|
import { toast } from 'sonner';
|
|
import { API } from '@/api/API';
|
|
import { formatDateTime } from '@/components/ui/utils';
|
|
import { ProspectiveApplicationDetails } from '@/features/onboarding/pages/ProspectiveApplicationDetails';
|
|
|
|
export function ProspectiveDashboardPage() {
|
|
const dispatch = useDispatch();
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const { user } = useSelector((state: RootState) => state.auth);
|
|
const [collapsed, setCollapsed] = useState(false);
|
|
const [activeTab, setActiveTab] = useState('applicant');
|
|
|
|
const handleLogout = () => {
|
|
dispatch(logout());
|
|
toast.info('Logged out successfully');
|
|
navigate('/');
|
|
};
|
|
|
|
return (
|
|
<div className="flex h-screen bg-slate-50">
|
|
{/* Sidebar */}
|
|
<div className={`bg-slate-900 text-white h-screen flex flex-col transition-all duration-300 ${collapsed ? 'w-20' : 'w-64'}`}>
|
|
<div className="p-4 border-b border-slate-800">
|
|
{!collapsed ? (
|
|
<div className="space-y-3">
|
|
<div className="flex justify-end">
|
|
<button
|
|
onClick={() => setCollapsed(true)}
|
|
className="p-1 hover:bg-slate-800 rounded transition-colors"
|
|
title="Collapse sidebar"
|
|
>
|
|
<ChevronLeft className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
<div className="w-full">
|
|
<img
|
|
src="/assets/images/Re_Logo.png"
|
|
alt="Royal Enfield"
|
|
className="mx-auto block h-auto w-full max-h-14 object-contain"
|
|
/>
|
|
</div>
|
|
<p className="text-center text-[10px] font-bold uppercase tracking-[0.2em] text-slate-400">
|
|
Applicant Portal
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-col items-center gap-3">
|
|
<div className="w-full">
|
|
<img
|
|
src="/assets/images/Re_Logo.png"
|
|
alt="Royal Enfield"
|
|
className="block h-auto w-full max-h-10 object-contain"
|
|
/>
|
|
</div>
|
|
<button
|
|
onClick={() => setCollapsed(false)}
|
|
className="p-1 hover:bg-slate-800 rounded transition-colors"
|
|
title="Expand sidebar"
|
|
>
|
|
<ChevronRight className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<nav className="flex-1 p-4 space-y-2">
|
|
<div>
|
|
<button
|
|
onClick={() => {
|
|
setActiveTab('applicant');
|
|
navigate('/prospective-dashboard');
|
|
}}
|
|
className={`w-full flex items-center gap-3 px-4 py-3 rounded-lg transition-colors ${activeTab === 'applicant' ? 'bg-re-red text-white hover:bg-re-red-hover' : 'text-slate-300 hover:bg-slate-800 hover:text-white'}`}
|
|
>
|
|
<FileText className="w-5 h-5 flex-shrink-0" />
|
|
{!collapsed && <span className="flex-1 text-left">My Applications</span>}
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
|
|
<div className="p-4 border-t border-slate-800 space-y-2">
|
|
{!collapsed && (
|
|
<div className="px-4 py-2 bg-slate-800 rounded-lg mb-2">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 bg-re-red rounded-full flex items-center justify-center text-white ring-2 ring-white/20">
|
|
<span className="font-bold">{user?.name?.charAt(0) || 'A'}</span>
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="truncate text-sm font-medium">{user?.name || 'Applicant'}</p>
|
|
<p className="text-slate-400 truncate text-xs">{user?.role || 'Prospective'}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
<button
|
|
onClick={handleLogout}
|
|
className={`w-full flex items-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all h-9 px-4 py-2 text-slate-300 hover:bg-slate-800 hover:text-white ${collapsed ? 'justify-center' : 'justify-start'}`}
|
|
>
|
|
<LogOut className="w-5 h-5 flex-shrink-0" />
|
|
{!collapsed && <span className="ml-3">Logout</span>}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main Content */}
|
|
<div className="flex-1 flex flex-col overflow-hidden">
|
|
<header className="bg-white border-b border-slate-200 px-6 py-4">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-slate-900 text-xl font-semibold">
|
|
{location.pathname.includes('/application/')
|
|
? 'Application details'
|
|
: 'Applicant management'}
|
|
</h1>
|
|
<p className="text-slate-600 text-sm max-w-2xl leading-snug">
|
|
{location.pathname.includes('/application/')
|
|
? 'Review and update statutory information and required documents for this application. Progress is coordinated by Royal Enfield after you submit.'
|
|
: 'Start or continue your dealership application, upload documents, and use links we email you (for example the questionnaire).'}
|
|
</p>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex items-center gap-3 px-3 py-2 bg-slate-100 rounded-lg">
|
|
<div className="w-8 h-8 bg-re-red rounded-full flex items-center justify-center ring-2 ring-re-red/20">
|
|
<User className="w-4 h-4 text-white" />
|
|
</div>
|
|
<div className="text-left">
|
|
<p className="text-slate-900 text-sm font-medium">{user?.name || 'Applicant'}</p>
|
|
<p className="text-slate-600 text-xs">{user?.role || 'User'}</p>
|
|
</div>
|
|
</div>
|
|
<button className="p-2 rounded-md hover:bg-slate-100" title="Refresh" onClick={() => window.location.reload()}>
|
|
<RefreshCw className="w-4 h-4 text-slate-600" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="flex-1 overflow-y-auto p-6">
|
|
<Routes>
|
|
<Route path="/" element={<ProspectiveApplicationList />} />
|
|
<Route path="/application/:id" element={<ProspectiveApplicationDetailsWrapper />} />
|
|
</Routes>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ProspectiveApplicationList() {
|
|
const navigate = useNavigate();
|
|
const { user } = useSelector((state: RootState) => state.auth);
|
|
const [applications, setApplications] = useState<any[]>([]);
|
|
|
|
useEffect(() => {
|
|
if (user?.id) {
|
|
fetchApplications();
|
|
}
|
|
}, [user?.id]);
|
|
|
|
const fetchApplications = async () => {
|
|
try {
|
|
const response: any = await API.getApplications();
|
|
if (response.data?.success) {
|
|
setApplications(response.data.data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch applications', error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="max-w-7xl mx-auto">
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between mb-8">
|
|
<div>
|
|
<h1 className="text-slate-900 text-3xl font-bold mb-2">My Applications</h1>
|
|
<p className="text-slate-500 font-medium">Track and manage your dealership applications</p>
|
|
</div>
|
|
</div>
|
|
|
|
{applications.length === 0 ? (
|
|
<div className="bg-white rounded-2xl border border-slate-200 border-dashed p-12 text-center">
|
|
<div className="w-16 h-16 bg-slate-50 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
<FileText className="w-8 h-8 text-slate-400" />
|
|
</div>
|
|
<h3 className="text-lg font-semibold text-slate-900 mb-1">No applications found</h3>
|
|
<p className="text-slate-500 max-w-sm mx-auto mb-6">
|
|
You haven't submitted any dealership applications yet.
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{applications.map((app) => (
|
|
<div
|
|
key={app.id}
|
|
className="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm hover:shadow-md hover:border-re-red cursor-pointer transition-all group"
|
|
onClick={() => navigate(`/prospective-dashboard/application/${app.id}`)}
|
|
>
|
|
<div className="mb-4">
|
|
<div className="w-12 h-12 bg-red-50 rounded-xl flex items-center justify-center group-hover:bg-re-red transition-colors">
|
|
<FileText className="w-6 h-6 text-re-red group-hover:text-white" />
|
|
</div>
|
|
</div>
|
|
<h3 className="text-xl font-bold text-slate-900 mb-1 truncate">{app.applicationId}</h3>
|
|
<p className="text-slate-500 text-sm mb-4 font-medium">{app.city}, {app.state}</p>
|
|
|
|
<div className="space-y-4 pt-6 border-t border-slate-100">
|
|
<div className="flex justify-between items-center">
|
|
<span className="text-xs text-slate-500 font-medium">Applied</span>
|
|
<span className="text-xs font-bold text-slate-600">{formatDateTime(app.createdAt)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ProspectiveApplicationDetailsWrapper() {
|
|
const { id } = useParams<{ id: string }>();
|
|
const navigate = useNavigate();
|
|
|
|
if (!id) return null;
|
|
|
|
return (
|
|
<div className="max-w-7xl mx-auto">
|
|
<ProspectiveApplicationDetails
|
|
id={id}
|
|
onBack={() => navigate('/prospective-dashboard')}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|