Dealer_Onboard_Frontend/src/components/applications/FnFPage.tsx

545 lines
25 KiB
TypeScript

import { useState, useEffect } from 'react';
import { IndianRupee, Calendar, Eye, Send, FileCheck, Loader2 } from 'lucide-react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card';
import { Badge } from '../ui/badge';
import { Button } from '../ui/button';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';
import { User } from '../../lib/mock-data';
import { API } from '../../api/API';
import { toast } from 'sonner';
interface FnFPageProps {
currentUser: User | null;
onViewDetails: (id: string) => void;
}
const getStatusColor = (status: string) => {
switch (status) {
case 'New':
return 'bg-blue-100 text-blue-700 border-blue-300';
case 'In Progress':
return 'bg-yellow-100 text-yellow-700 border-yellow-300';
case 'Under Review':
return 'bg-orange-100 text-orange-700 border-orange-300';
case 'Completed':
return 'bg-green-100 text-green-700 border-green-300';
default:
return 'bg-slate-100 text-slate-700 border-slate-300';
}
};
const getTypeColor = (type: string) => {
return type === 'Resignation'
? 'bg-amber-100 text-amber-700 border-amber-300'
: 'bg-red-100 text-red-700 border-red-300';
};
export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
const [settlements, setSettlements] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const canSendToStakeholders = currentUser &&
['DD Lead', 'DD Head', 'NBH', 'DD Admin', 'Super Admin'].includes(currentUser.role);
useEffect(() => {
fetchSettlements();
}, []);
const fetchSettlements = async () => {
try {
setLoading(true);
const response = await API.getFnFSettlements();
const data = response.data as any;
if (data.success) {
setSettlements(data.settlements || []);
}
} catch (error) {
console.error('Fetch settlements error:', error);
toast.error('Failed to fetch settlement cases');
} finally {
setLoading(false);
}
};
const handleSendToStakeholders = (caseId: string) => {
console.log('Sending to stakeholders for case:', caseId);
toast.success('Notifications sent to all stakeholders');
};
if (loading) {
return (
<div className="flex items-center justify-center p-12">
<Loader2 className="w-8 h-8 animate-spin text-blue-600" />
</div>
);
}
// Helper to map backend data to UI-friendly format
const getMappedData = (s: any) => ({
id: s.id,
caseNumber: s.id.substring(0, 8).toUpperCase(),
status: s.status,
requestType: s.resignationId ? 'Resignation' : 'Termination',
dealerName: s.outlet?.dealer?.name || 'N/A',
dealerCode: s.outlet?.code || 'N/A',
dealershipName: s.outlet?.name || 'N/A',
location: s.outlet?.city || s.outlet?.location || 'N/A',
originalRequestId: s.resignation?.resignationId || s.terminationRequest?.id || 'N/A',
submittedOn: new Date(s.createdAt).toLocaleDateString(),
financeReportStatus: s.status === 'Calculated' || s.status === 'Settled' ? 'Completed' : 'Pending',
totalRecoveryAmount: parseFloat(s.totalReceivables) || 0,
totalPayableAmount: parseFloat(s.totalPayables) || 0,
completedOn: s.settlementDate ? new Date(s.settlementDate).toLocaleDateString() : null,
departmentResponses: s.lineItems || []
});
const displaySettlements: any[] = settlements.map(getMappedData);
return (
<div className="space-y-6">
{/* Header Stats */}
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
<Card>
<CardHeader className="pb-3">
<CardDescription>New Cases</CardDescription>
<CardTitle className="text-3xl text-blue-600">
{displaySettlements.filter(c => c.status === 'Initiated' || c.status === 'New').length}
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-slate-600">Just Arrived</p>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>In Progress</CardDescription>
<CardTitle className="text-3xl text-yellow-600">
{displaySettlements.filter(c => c.status === 'In Progress').length}
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-slate-600">Awaiting Response</p>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Under Review</CardDescription>
<CardTitle className="text-3xl text-orange-600">
{displaySettlements.filter(c => c.status === 'Under Review' || c.status === 'Calculated').length}
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-slate-600">Discussion Ongoing</p>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Completed</CardDescription>
<CardTitle className="text-3xl text-green-600">
{displaySettlements.filter(c => c.status === 'Completed' || c.status === 'Settled').length}
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-slate-600">Finalized</p>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>All Cases</CardDescription>
<CardTitle className="text-3xl">{displaySettlements.length}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-slate-600">Total</p>
</CardContent>
</Card>
</div>
{/* Main Content */}
<Card>
<CardHeader>
<CardTitle>Full & Final Settlement Cases</CardTitle>
<CardDescription>
Manage dealer exit dues clearance and settlement
{currentUser && ` • Current Role: ${currentUser.role}`}
</CardDescription>
</CardHeader>
<CardContent>
<Tabs defaultValue="all" className="w-full">
<TabsList>
<TabsTrigger value="new">New Cases</TabsTrigger>
<TabsTrigger value="all">All Cases</TabsTrigger>
<TabsTrigger value="progress">In Progress</TabsTrigger>
<TabsTrigger value="review">Under Review</TabsTrigger>
<TabsTrigger value="completed">Completed</TabsTrigger>
</TabsList>
{/* New Cases Tab */}
<TabsContent value="new" className="mt-6">
<div className="space-y-4">
{displaySettlements
.filter(c => c.status === 'New' || c.status === 'Initiated')
.map((fnfCase) => (
<Card key={fnfCase.id} className="border-slate-200">
<CardContent className="pt-6">
<div className="flex items-start justify-between">
<div className="flex items-start gap-4 flex-1">
<div className="p-3 bg-blue-100 rounded-lg">
<FileCheck className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h3 className="text-lg">{fnfCase.caseNumber}</h3>
<Badge className={getStatusColor(fnfCase.status)}>
{fnfCase.status}
</Badge>
<Badge className={getTypeColor(fnfCase.requestType)}>
{fnfCase.requestType}
</Badge>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
<div>
<p className="text-slate-600">Dealer Name</p>
<p>{fnfCase.dealerName}</p>
</div>
<div>
<p className="text-slate-600">Dealer Code</p>
<p>{fnfCase.dealerCode}</p>
</div>
<div>
<p className="text-slate-600">Dealership Name</p>
<p>{fnfCase.dealershipName}</p>
</div>
<div>
<p className="text-slate-600">Location</p>
<p>{fnfCase.location}</p>
</div>
<div>
<p className="text-slate-600">Original Request ID</p>
<p>{fnfCase.originalRequestId}</p>
</div>
<div>
<p className="text-slate-600">Submitted On</p>
<div className="flex items-center gap-1">
<Calendar className="w-4 h-4 text-slate-500" />
<p>{fnfCase.submittedOn}</p>
</div>
</div>
<div>
<p className="text-slate-600">Finance Report</p>
<p>{fnfCase.financeReportStatus}</p>
</div>
</div>
</div>
</div>
<div className="flex items-center gap-2 ml-4">
{canSendToStakeholders && (
<Button
size="sm"
variant="outline"
className="text-blue-600 border-blue-300 hover:bg-blue-50"
onClick={() => handleSendToStakeholders(fnfCase.id)}
>
<Send className="w-4 h-4 mr-2" />
Send to Stakeholders
</Button>
)}
<Button
size="sm"
variant="outline"
onClick={() => onViewDetails(fnfCase.id)}
>
<Eye className="w-4 h-4 mr-2" />
View Details
</Button>
</div>
</div>
</CardContent>
</Card>
))}
{displaySettlements.filter((c: any) => c.status === 'New' || c.status === 'Initiated').length === 0 && (
<div className="text-center py-12 text-slate-500">
<FileCheck className="w-12 h-12 mx-auto mb-4 text-slate-400" />
<p>No new cases to display</p>
</div>
)}
</div>
</TabsContent>
{/* All Cases Tab */}
<TabsContent value="all" className="mt-6">
<div className="space-y-4">
{displaySettlements.map((fnfCase) => (
<Card key={fnfCase.id} className="border-slate-200">
<CardContent className="pt-6">
<div className="flex items-start justify-between">
<div className="flex items-start gap-4 flex-1">
<div className={`p-3 rounded-lg ${
fnfCase.status === 'New' ? 'bg-blue-100' :
fnfCase.status === 'In Progress' ? 'bg-yellow-100' :
fnfCase.status === 'Under Review' ? 'bg-orange-100' :
'bg-green-100'
}`}>
<IndianRupee className={`w-6 h-6 ${
fnfCase.status === 'New' ? 'text-blue-600' :
fnfCase.status === 'In Progress' ? 'text-yellow-600' :
fnfCase.status === 'Under Review' ? 'text-orange-600' :
'text-green-600'
}`} />
</div>
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h3 className="text-lg">{fnfCase.caseNumber}</h3>
<Badge className={getStatusColor(fnfCase.status)}>
{fnfCase.status}
</Badge>
<Badge className={getTypeColor(fnfCase.requestType)}>
{fnfCase.requestType}
</Badge>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
<div>
<p className="text-slate-600">Dealer Name</p>
<p>{fnfCase.dealerName}</p>
</div>
<div>
<p className="text-slate-600">Dealership Name</p>
<p>{fnfCase.dealershipName}</p>
</div>
<div>
<p className="text-slate-600">Location</p>
<p>{fnfCase.location}</p>
</div>
<div>
<p className="text-slate-600">Submitted On</p>
<p>{fnfCase.submittedOn}</p>
</div>
</div>
</div>
</div>
<div className="flex items-center gap-2 ml-4">
{canSendToStakeholders && fnfCase.status === 'New' && (
<Button
size="sm"
variant="outline"
className="text-blue-600 border-blue-300 hover:bg-blue-50"
onClick={() => handleSendToStakeholders(fnfCase.id)}
>
<Send className="w-4 h-4 mr-2" />
Send to Stakeholders
</Button>
)}
<Button
size="sm"
variant="outline"
onClick={() => onViewDetails(fnfCase.id)}
>
<Eye className="w-4 h-4 mr-2" />
View Details
</Button>
</div>
</div>
</CardContent>
</Card>
))}
</div>
</TabsContent>
{/* In Progress Tab */}
<TabsContent value="progress" className="mt-6">
<div className="space-y-4">
{displaySettlements
.filter(c => c.status === 'In Progress')
.map((fnfCase) => (
<Card key={fnfCase.id} className="border-slate-200">
<CardContent className="pt-6">
<div className="flex items-start justify-between">
<div className="flex items-start gap-4 flex-1">
<div className="p-3 bg-yellow-100 rounded-lg">
<IndianRupee className="w-6 h-6 text-yellow-600" />
</div>
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h3 className="text-lg">{fnfCase.caseNumber}</h3>
<Badge className={getStatusColor(fnfCase.status)}>
{fnfCase.status}
</Badge>
<Badge className={getTypeColor(fnfCase.requestType)}>
{fnfCase.requestType}
</Badge>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 text-sm">
<div>
<p className="text-slate-600">Dealer Name</p>
<p>{fnfCase.dealerName}</p>
</div>
<div>
<p className="text-slate-600">Departments Responded</p>
<p>
{fnfCase.departmentResponses.filter((d: any) => d.status !== 'Pending').length} / {fnfCase.departmentResponses.length}
</p>
</div>
<div>
<p className="text-slate-600">Submitted On</p>
<p>{fnfCase.submittedOn}</p>
</div>
</div>
</div>
</div>
<Button
size="sm"
variant="outline"
onClick={() => onViewDetails(fnfCase.id)}
className="ml-4"
>
<Eye className="w-4 h-4 mr-2" />
View Details
</Button>
</div>
</CardContent>
</Card>
))}
{displaySettlements.filter((c: any) => c.status === 'In Progress').length === 0 && (
<div className="text-center py-12 text-slate-500">
<IndianRupee className="w-12 h-12 mx-auto mb-4 text-slate-400" />
<p>No cases in progress</p>
</div>
)}
</div>
</TabsContent>
{/* Under Review Tab */}
<TabsContent value="review" className="mt-6">
<div className="space-y-4">
{displaySettlements
.filter(c => c.status === 'Under Review' || c.status === 'Calculated')
.map((fnfCase) => (
<Card key={fnfCase.id} className="border-slate-200">
<CardContent className="pt-6">
<div className="flex items-start justify-between">
<div className="flex items-start gap-4 flex-1">
<div className="p-3 bg-orange-100 rounded-lg">
<IndianRupee className="w-6 h-6 text-orange-600" />
</div>
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h3 className="text-lg">{fnfCase.caseNumber}</h3>
<Badge className={getStatusColor(fnfCase.status)}>
{fnfCase.status}
</Badge>
<Badge className={getTypeColor(fnfCase.requestType)}>
{fnfCase.requestType}
</Badge>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
<div>
<p className="text-slate-600">Dealer Name</p>
<p>{fnfCase.dealerName}</p>
</div>
<div>
<p className="text-slate-600">Recovery Amount</p>
<p className="text-red-600">{fnfCase.totalRecoveryAmount?.toLocaleString()}</p>
</div>
<div>
<p className="text-slate-600">Payable Amount</p>
<p className="text-green-600">{fnfCase.totalPayableAmount?.toLocaleString()}</p>
</div>
<div>
<p className="text-slate-600">Finance Status</p>
<p>{fnfCase.financeReportStatus}</p>
</div>
</div>
</div>
</div>
<Button
size="sm"
variant="outline"
onClick={() => onViewDetails(fnfCase.id)}
className="ml-4"
>
<Eye className="w-4 h-4 mr-2" />
View Details
</Button>
</div>
</CardContent>
</Card>
))}
{displaySettlements.filter(c => c.status === 'Under Review' || c.status === 'Calculated').length === 0 && (
<div className="text-center py-12 text-slate-500">
<IndianRupee className="w-12 h-12 mx-auto mb-4 text-slate-400" />
<p>No cases under review</p>
</div>
)}
</div>
</TabsContent>
{/* Completed Tab */}
<TabsContent value="completed" className="mt-6">
<div className="space-y-4">
{displaySettlements
.filter(c => c.status === 'Completed' || c.status === 'Settled')
.map((fnfCase) => (
<Card key={fnfCase.id} className="border-slate-200">
<CardContent className="pt-6">
<div className="flex items-start justify-between">
<div className="flex items-start gap-4 flex-1">
<div className="p-3 bg-green-100 rounded-lg">
<FileCheck className="w-6 h-6 text-green-600" />
</div>
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h3 className="text-lg">{fnfCase.caseNumber}</h3>
<Badge className={getStatusColor(fnfCase.status)}>
{fnfCase.status}
</Badge>
<Badge className={getTypeColor(fnfCase.requestType)}>
{fnfCase.requestType}
</Badge>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 text-sm">
<div>
<p className="text-slate-600">Dealer Name</p>
<p>{fnfCase.dealerName}</p>
</div>
<div>
<p className="text-slate-600">Completed On</p>
<p>{fnfCase.completedOn || 'N/A'}</p>
</div>
<div>
<p className="text-slate-600">Submitted On</p>
<p>{fnfCase.submittedOn}</p>
</div>
</div>
</div>
</div>
<Button
size="sm"
variant="outline"
onClick={() => onViewDetails(fnfCase.id)}
className="ml-4"
>
<Eye className="w-4 h-4 mr-2" />
View Details
</Button>
</div>
</CardContent>
</Card>
))}
{displaySettlements.filter(c => c.status === 'Completed' || c.status === 'Settled').length === 0 && (
<div className="text-center py-12 text-slate-500">
<FileCheck className="w-12 h-12 mx-auto mb-4 text-slate-400" />
<p>No completed cases</p>
</div>
)}
</div>
</TabsContent>
</Tabs>
</CardContent>
</Card>
</div>
);
}