Dealer_Onboard_Frontend/src/features/resignation/pages/ResignationPage.tsx

388 lines
18 KiB
TypeScript

import { FileText, Calendar, Eye } from 'lucide-react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { useState, useEffect } from 'react';
import { API } from '@/api/API';
import { toast } from 'sonner';
import { User as UserType } from '@/lib/mock-data';
import { formatDateTime } from '@/components/ui/utils';
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
interface ResignationPageProps {
currentUser: UserType | null;
onViewDetails: (id: string) => void;
}
const getStatusColor = (status: string) => {
if (status.includes('Approved') || status.includes('Completed')) return 'bg-green-100 text-green-700 border-green-300';
if (status.includes('Review') || status.includes('Pending')) return 'bg-yellow-100 text-yellow-700 border-yellow-300';
if (status.includes('Rejected')) return 'bg-red-100 text-red-700 border-red-300';
return 'bg-blue-100 text-blue-700 border-blue-300';
};
export function ResignationPage({ currentUser, onViewDetails }: ResignationPageProps) {
const [resignations, setResignations] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [currentPage, setCurrentPage] = useState(1);
const [statusTab, setStatusTab] = useState('all');
const [paginationMeta, setPaginationMeta] = useState<any>(null);
const itemsPerPage = 10;
const fetchResignations = async () => {
setLoading(true);
try {
const response = await API.getResignations({
page: currentPage,
limit: itemsPerPage,
status: statusTab === 'all' ? undefined : statusTab === 'open' ? 'open' : 'Completed,Closed'
});
const data = response.data as any;
if (data?.success) {
setResignations(data.requests || data.resignations?.rows || data.resignations || []);
setPaginationMeta(data.meta);
}
} catch (error) {
console.error('Error fetching resignations:', error);
toast.error('Failed to fetch resignation requests');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchResignations();
}, [currentPage, statusTab]);
const handleTabChange = (value: string) => {
setStatusTab(value);
setCurrentPage(1);
};
const openRequests = statusTab === 'open' ? resignations : [];
const completedRequests = statusTab === 'completed' ? resignations : [];
return (
<div className="space-y-6">
{/* Header Stats */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card>
<CardHeader className="pb-3">
<CardDescription>All Requests</CardDescription>
<CardTitle className="text-3xl">{paginationMeta?.total || 0}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-slate-600">Total Requests</p>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Open</CardDescription>
<CardTitle className="text-3xl text-yellow-600">{statusTab === 'open' ? paginationMeta?.total || 0 : '...'}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-slate-600">Requires Your Action</p>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Completed</CardDescription>
<CardTitle className="text-3xl text-green-600">{statusTab === 'completed' ? paginationMeta?.total || 0 : '...'}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-slate-600">Finalized</p>
</CardContent>
</Card>
</div>
{/* Main Content */}
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle>Resignation Requests</CardTitle>
<CardDescription>
Track and manage dealer resignation requests
<span className="block mt-1 text-slate-500">
Note: Resignation requests are initiated by the dealer or via ASM.
</span>
</CardDescription>
</div>
</div>
</CardHeader>
<CardContent>
<Tabs value={statusTab} onValueChange={handleTabChange} className="w-full">
<TabsList>
<TabsTrigger value="all">All Requests</TabsTrigger>
<TabsTrigger value="open">Open</TabsTrigger>
<TabsTrigger value="completed">Completed</TabsTrigger>
</TabsList>
<TabsContent value="all" className="mt-6">
<div className="space-y-4 text-center py-1">
{loading ? (
<div className="text-center py-12">Loading requests...</div>
) : resignations.length > 0 ? (
<>
{resignations.map((request) => (
<Card key={request.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-amber-100 rounded-lg">
<FileText className="w-6 h-6 text-amber-600" />
</div>
<div className="flex-1 text-left">
<div className="flex items-center gap-3 mb-2">
<h3 className="text-lg">{request.resignationId}</h3>
<Badge className={getStatusColor(request.status)}>
{request.status}
</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>{request.dealer?.dealerProfile?.businessName || request.outlet?.name || 'N/A'}</p>
</div>
<div>
<p className="text-slate-600">Dealer Code</p>
<p>{request.dealer?.dealerProfile?.dealerCode?.dealerCode || request.outlet?.code || 'N/A'}</p>
</div>
<div>
<p className="text-slate-600">Location</p>
<p>{request.dealer?.dealerProfile?.registeredAddress || (request.outlet?.city && request.outlet?.state ? `${request.outlet.city}, ${request.outlet.state}` : 'N/A')}</p>
</div>
<div>
<p className="text-slate-600">Type</p>
<p>{request.resignationType}</p>
</div>
<div>
<p className="text-slate-600">Reason</p>
<p className="truncate max-w-[200px]">{request.reason}</p>
</div>
<div>
<p className="text-slate-600">Current Stage</p>
<p>{request.currentStage}</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>{formatDateTime(request.submittedOn)}</p>
</div>
</div>
</div>
</div>
</div>
<Button
size="sm"
variant="outline"
onClick={() => onViewDetails(request.id)}
className="ml-4"
>
<Eye className="w-4 h-4 mr-2" />
View Details
</Button>
</div>
</CardContent>
</Card>
))}
{paginationMeta && paginationMeta.totalPages > 1 && (
<div className="py-4 border-t flex justify-center">
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
className={currentPage === 1 ? "pointer-events-none opacity-50" : "cursor-pointer"}
/>
</PaginationItem>
{[...Array(paginationMeta.totalPages)].map((_, i) => {
const pageNum = i + 1;
if (
pageNum === 1 ||
pageNum === paginationMeta.totalPages ||
(pageNum >= currentPage - 1 && pageNum <= currentPage + 1)
) {
return (
<PaginationItem key={pageNum}>
<PaginationLink
isActive={currentPage === pageNum}
onClick={() => setCurrentPage(pageNum)}
className="cursor-pointer"
>
{pageNum}
</PaginationLink>
</PaginationItem>
);
}
if (
(pageNum === 2 && currentPage > 3) ||
(pageNum === paginationMeta.totalPages - 1 && currentPage < paginationMeta.totalPages - 2)
) {
return <PaginationItem key={pageNum}><PaginationEllipsis /></PaginationItem>;
}
return null;
})}
<PaginationItem>
<PaginationNext
onClick={() => setCurrentPage(prev => Math.min(paginationMeta.totalPages, prev + 1))}
className={currentPage === paginationMeta.totalPages ? "pointer-events-none opacity-50" : "cursor-pointer"}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
)}
</>
) : (
<div className="text-center py-12 text-slate-500">
<p>No resignation requests found</p>
</div>
)}
</div>
</TabsContent>
{/* Open Tab */}
<TabsContent value="open" className="mt-6">
<div className="space-y-4">
{openRequests.length > 0 ? (
openRequests.map((request) => (
<Card key={request.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">
<FileText 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">{request.resignationId}</h3>
<Badge className={getStatusColor(request.status)}>
{request.status}
</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>{request.dealer?.dealerProfile?.businessName || request.outlet?.name || 'N/A'}</p>
</div>
<div>
<p className="text-slate-600">Location</p>
<p>{request.dealer?.dealerProfile?.registeredAddress || (request.outlet?.city && request.outlet?.state ? `${request.outlet.city}, ${request.outlet.state}` : 'N/A')}</p>
</div>
<div>
<p className="text-slate-600">Current Stage</p>
<p>{request.currentStage}</p>
</div>
<div>
<p className="text-slate-600">Submitted On</p>
<p>{formatDateTime(request.submittedOn)}</p>
</div>
</div>
</div>
</div>
<Button
size="sm"
variant="outline"
onClick={() => onViewDetails(request.id)}
className="ml-4"
>
<Eye className="w-4 h-4 mr-2" />
View Details
</Button>
</div>
</CardContent>
</Card>
))
) : (
<div className="text-center py-12 text-slate-500">
<FileText className="w-12 h-12 mx-auto mb-4 text-slate-400" />
<p>No requests requiring your action</p>
</div>
)}
</div>
</TabsContent>
{/* Completed Tab */}
<TabsContent value="completed" className="mt-6">
<div className="space-y-4">
{completedRequests.length > 0 ? (
completedRequests.map((request) => (
<Card key={request.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">
<FileText 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">{request.resignationId}</h3>
<Badge className={getStatusColor(request.status)}>
{request.status}
</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>{request.dealer?.dealerProfile?.businessName || request.outlet?.name || 'N/A'}</p>
</div>
<div>
<p className="text-slate-600">Location</p>
<p>{request.dealer?.dealerProfile?.registeredAddress || request.outlet?.city || 'N/A'}</p>
</div>
<div>
<p className="text-slate-600">Final Stage</p>
<p>{request.currentStage}</p>
</div>
<div>
<p className="text-slate-600">Submitted On</p>
<p>{formatDateTime(request.submittedOn)}</p>
</div>
</div>
</div>
</div>
<Button
size="sm"
variant="outline"
onClick={() => onViewDetails(request.id)}
className="ml-4"
>
<Eye className="w-4 h-4 mr-2" />
View Details
</Button>
</div>
</CardContent>
</Card>
))
) : (
<div className="text-center py-12 text-slate-500">
<FileText className="w-12 h-12 mx-auto mb-4 text-slate-400" />
<p>No completed resignations to display</p>
</div>
)}
</div>
</TabsContent>
</Tabs>
</CardContent>
</Card>
</div>
);
}