Dealer_Onboard_Frontend/src/features/dashboard/pages/ProspectiveDashboardPage.tsx

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>
);
}