711 lines
30 KiB
TypeScript
711 lines
30 KiB
TypeScript
import { FileText, Calendar, Building, Plus, Eye, ArrowRight, Shield, 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 { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../ui/table';
|
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '../ui/dialog';
|
|
import { Input } from '../ui/input';
|
|
import { Label } from '../ui/label';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select';
|
|
import { Textarea } from '../ui/textarea';
|
|
import { useState, useEffect } from 'react';
|
|
import { User as UserType } from '../../lib/mock-data';
|
|
import { toast } from 'sonner';
|
|
import { API } from '../../api/API';
|
|
|
|
interface ConstitutionalChangePageProps {
|
|
currentUser: UserType | null;
|
|
onViewDetails: (id: string) => void;
|
|
}
|
|
|
|
// Document requirements mapping
|
|
const documentRequirements: Record<string, number[]> = {
|
|
'Partnership': [1, 2, 3, 4, 8, 9, 10, 16],
|
|
'LLP': [1, 2, 3, 7, 8, 9, 10, 16],
|
|
'Pvt Ltd': [1, 2, 3, 5, 6, 7, 8, 10, 16],
|
|
'Proprietorship': [1, 2, 3, 10, 16]
|
|
};
|
|
|
|
// Document names
|
|
const documentNames: Record<number, string> = {
|
|
1: 'GST',
|
|
2: 'Firm Pan Copy',
|
|
3: 'Self attested KYC\'s',
|
|
4: 'Partnership Agreement (Notarised)',
|
|
5: 'MOA (Applicable for Only Pvt.Ltd)',
|
|
6: 'AOA (Applicable for Only Pvt.Ltd)',
|
|
7: 'COI (Applicable for Only Pvt.Ltd & LLP)',
|
|
8: 'BPA - Business Purchase Agreement',
|
|
9: 'Firm Registration Certificate (Partnership)',
|
|
10: 'Cancelled Cheque',
|
|
11: 'LLP Agreement (Notarised)',
|
|
12: 'ZBH Approval',
|
|
13: 'NBH Approval',
|
|
14: 'RBM Approval',
|
|
15: 'DD-Lead Approval',
|
|
16: 'Declaration / Authorization Letter'
|
|
};
|
|
|
|
const getStatusColor = (status: string) => {
|
|
if (status === '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';
|
|
if (status.includes('Collection')) return 'bg-blue-100 text-blue-700 border-blue-300';
|
|
return 'bg-slate-100 text-slate-700 border-slate-300';
|
|
};
|
|
|
|
const getTypeColor = (type: string) => {
|
|
switch(type) {
|
|
case 'Proprietorship': return 'bg-purple-100 text-purple-700 border-purple-300';
|
|
case 'Partnership': return 'bg-blue-100 text-blue-700 border-blue-300';
|
|
case 'LLP': return 'bg-indigo-100 text-indigo-700 border-indigo-300';
|
|
case 'Pvt Ltd': return 'bg-cyan-100 text-cyan-700 border-cyan-300';
|
|
default: return 'bg-slate-100 text-slate-700 border-slate-300';
|
|
}
|
|
};
|
|
|
|
export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChangePageProps) {
|
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
const [dealerCode, setDealerCode] = useState('');
|
|
const [dealerData, setDealerData] = useState<any>(null);
|
|
const [targetType, setTargetType] = useState('');
|
|
const [reason, setReason] = useState('');
|
|
const [requiredDocs, setRequiredDocs] = useState<number[]>([]);
|
|
const [requests, setRequests] = useState<any[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
useEffect(() => {
|
|
fetchRequests();
|
|
}, []);
|
|
|
|
const fetchRequests = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
const response = await API.getConstitutionalChanges() as any;
|
|
if (response.data.success) {
|
|
setRequests(response.data.requests || []);
|
|
}
|
|
} catch (error) {
|
|
console.error('Fetch requests error:', error);
|
|
toast.error('Failed to fetch requests');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleDealerCodeChange = async (code: string) => {
|
|
setDealerCode(code);
|
|
if (code.length >= 5) {
|
|
try {
|
|
const response = await API.getOutletByCode(code) as any;
|
|
if (response.data.success && response.data.outlet) {
|
|
const outlet = response.data.outlet;
|
|
setDealerData({
|
|
id: outlet.id,
|
|
dealerName: outlet.name,
|
|
address: outlet.address,
|
|
dealershipName: outlet.name,
|
|
gst: outlet.gstNumber || 'N/A',
|
|
currentType: outlet.type || 'Proprietorship',
|
|
region: outlet.region || 'N/A',
|
|
zone: outlet.zone || 'N/A'
|
|
});
|
|
toast.success('Dealer details loaded successfully');
|
|
} else {
|
|
setDealerData(null);
|
|
}
|
|
} catch (error) {
|
|
setDealerData(null);
|
|
}
|
|
} else {
|
|
setDealerData(null);
|
|
}
|
|
};
|
|
|
|
const handleTargetTypeChange = (type: string) => {
|
|
setTargetType(type);
|
|
setRequiredDocs(documentRequirements[type] || []);
|
|
};
|
|
|
|
const handleSubmitRequest = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (!dealerData) {
|
|
toast.error('Please enter a valid dealer code');
|
|
return;
|
|
}
|
|
|
|
if (!targetType) {
|
|
toast.error('Please select target dealership type');
|
|
return;
|
|
}
|
|
|
|
if (!reason.trim()) {
|
|
toast.error('Please provide a reason for constitutional change');
|
|
return;
|
|
}
|
|
|
|
if (dealerData.currentType === targetType) {
|
|
toast.error('Target type cannot be same as current type');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setIsSubmitting(true);
|
|
const payload = {
|
|
outletId: dealerData.id,
|
|
changeType: targetType,
|
|
description: reason,
|
|
newEntityDetails: {}
|
|
};
|
|
|
|
const response = await API.createConstitutionalChange(payload) as any;
|
|
if (response.data.success) {
|
|
toast.success('Constitutional change request submitted successfully');
|
|
setIsDialogOpen(false);
|
|
fetchRequests();
|
|
|
|
// Reset form
|
|
setDealerCode('');
|
|
setDealerData(null);
|
|
setTargetType('');
|
|
setReason('');
|
|
setRequiredDocs([]);
|
|
}
|
|
} catch (error) {
|
|
console.error('Submit request error:', error);
|
|
toast.error('Failed to submit request');
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
// Statistics
|
|
const stats = [
|
|
{
|
|
title: 'Total Requests',
|
|
value: requests.length,
|
|
icon: FileText,
|
|
color: 'bg-blue-500',
|
|
},
|
|
{
|
|
title: 'In Progress',
|
|
value: requests.filter(r => r.status !== 'Completed' && !r.status.includes('Rejected')).length,
|
|
icon: Calendar,
|
|
color: 'bg-yellow-500',
|
|
},
|
|
{
|
|
title: 'Completed',
|
|
value: requests.filter(r => r.status === 'Completed').length,
|
|
icon: Shield,
|
|
color: 'bg-green-500',
|
|
},
|
|
{
|
|
title: 'Pending Action',
|
|
value: requests.filter(r => r.status.includes('Review') || r.status.includes('Pending')).length,
|
|
icon: Building,
|
|
color: 'bg-amber-500',
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Loading Overlay */}
|
|
{isLoading && (
|
|
<div className="fixed inset-0 bg-slate-900/20 backdrop-blur-sm z-50 flex items-center justify-center">
|
|
<Loader2 className="w-8 h-8 text-amber-600 animate-spin" />
|
|
</div>
|
|
)}
|
|
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-slate-900 mb-2">Constitutional Change Management</h1>
|
|
<p className="text-slate-600">
|
|
Manage dealership constitutional change requests - Adding/Removing partners or changing business structure
|
|
</p>
|
|
</div>
|
|
|
|
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
|
<DialogTrigger asChild>
|
|
<Button className="bg-amber-600 hover:bg-amber-700">
|
|
<Plus className="w-4 h-4 mr-2" />
|
|
New Request
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle>Create Constitutional Change Request</DialogTitle>
|
|
<DialogDescription>
|
|
Submit a request for dealership constitutional change. All fields are mandatory.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<form onSubmit={handleSubmitRequest} className="space-y-4">
|
|
{/* Dealer Code */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="dealerCode">Dealer Code *</Label>
|
|
<Input
|
|
id="dealerCode"
|
|
placeholder="Enter dealer code (e.g., DL-MH-001)"
|
|
value={dealerCode}
|
|
onChange={(e) => handleDealerCodeChange(e.target.value)}
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
{/* Auto-populated Dealer Details */}
|
|
{dealerData && (
|
|
<div className="bg-slate-50 border border-slate-200 rounded-lg p-4 space-y-3">
|
|
<h3 className="text-slate-900">Dealer Details</h3>
|
|
<div className="grid grid-cols-2 gap-3 text-sm">
|
|
<div>
|
|
<span className="text-slate-600">Dealer Name:</span>
|
|
<p className="text-slate-900">{dealerData.dealerName}</p>
|
|
</div>
|
|
<div>
|
|
<span className="text-slate-600">Dealership Name:</span>
|
|
<p className="text-slate-900">{dealerData.dealershipName}</p>
|
|
</div>
|
|
<div>
|
|
<span className="text-slate-600">Location:</span>
|
|
<p className="text-slate-900">{dealerData.address}</p>
|
|
</div>
|
|
<div>
|
|
<span className="text-slate-600">GST:</span>
|
|
<p className="text-slate-900">{dealerData.gst}</p>
|
|
</div>
|
|
<div>
|
|
<span className="text-slate-600">Current Type:</span>
|
|
<Badge className={getTypeColor(dealerData.currentType)}>
|
|
{dealerData.currentType}
|
|
</Badge>
|
|
</div>
|
|
<div>
|
|
<span className="text-slate-600">Region/Zone:</span>
|
|
<p className="text-slate-900">{dealerData.region} / {dealerData.zone}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Target Dealership Type */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="targetType">Target Dealership Type *</Label>
|
|
<Select value={targetType} onValueChange={handleTargetTypeChange} required>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select target dealership type" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="Proprietorship">Proprietorship</SelectItem>
|
|
<SelectItem value="Partnership">Partnership</SelectItem>
|
|
<SelectItem value="LLP">LLP (Limited Liability Partnership)</SelectItem>
|
|
<SelectItem value="Pvt Ltd">Pvt Ltd (Private Limited)</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
{dealerData && targetType && dealerData.currentType === targetType && (
|
|
<p className="text-red-600 text-sm">Target type cannot be same as current type</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Required Documents Display */}
|
|
{targetType && requiredDocs.length > 0 && (
|
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 space-y-2">
|
|
<h4 className="text-blue-900">Required Documents for {targetType}</h4>
|
|
<div className="grid grid-cols-1 gap-2">
|
|
{requiredDocs.map((docNum) => (
|
|
<div key={docNum} className="flex items-start gap-2 text-sm">
|
|
<span className="text-blue-600 font-medium">{docNum}.</span>
|
|
<span className="text-blue-800">{documentNames[docNum]}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Reason */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="reason">Reason for Constitutional Change *</Label>
|
|
<Textarea
|
|
id="reason"
|
|
placeholder="Provide detailed reason for the constitutional change request..."
|
|
value={reason}
|
|
onChange={(e) => setReason(e.target.value)}
|
|
rows={4}
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={() => setIsDialogOpen(false)}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
className="bg-amber-600 hover:bg-amber-700"
|
|
disabled={!dealerData || !targetType || (dealerData && dealerData.currentType === targetType) || isSubmitting}
|
|
>
|
|
{isSubmitting ? (
|
|
<>
|
|
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
|
Submitting...
|
|
</>
|
|
) : (
|
|
'Submit Request'
|
|
)}
|
|
</Button>
|
|
</DialogFooter>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</div>
|
|
|
|
{/* Statistics Cards */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{stats.map((stat, index) => {
|
|
const Icon = stat.icon;
|
|
return (
|
|
<Card key={index}>
|
|
<CardContent className="p-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<p className="text-slate-600 text-sm">{stat.title}</p>
|
|
<p className="text-slate-900 text-2xl mt-1">{stat.value}</p>
|
|
</div>
|
|
<div className={`${stat.color} w-12 h-12 rounded-lg flex items-center justify-center`}>
|
|
<Icon className="w-6 h-6 text-white" />
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* Requests Table */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Constitutional Change Requests</CardTitle>
|
|
<CardDescription>
|
|
Track and manage all constitutional change requests across all stages
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Tabs defaultValue="all" className="w-full">
|
|
<TabsList className="grid w-full grid-cols-4">
|
|
<TabsTrigger value="all">All Requests</TabsTrigger>
|
|
<TabsTrigger value="pending">Pending</TabsTrigger>
|
|
<TabsTrigger value="in-progress">In Progress</TabsTrigger>
|
|
<TabsTrigger value="completed">Completed</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="all" className="mt-4">
|
|
<div className="border border-slate-200 rounded-lg overflow-hidden">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow className="bg-slate-50">
|
|
<TableHead>Request ID</TableHead>
|
|
<TableHead>Dealer Details</TableHead>
|
|
<TableHead>Constitutional Change</TableHead>
|
|
<TableHead>Current Stage</TableHead>
|
|
<TableHead>Progress</TableHead>
|
|
<TableHead>Submitted On</TableHead>
|
|
<TableHead>Actions</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{requests.length === 0 ? (
|
|
<TableRow>
|
|
<TableCell colSpan={7} className="text-center py-8 text-slate-500">
|
|
No constitutional change requests found
|
|
</TableCell>
|
|
</TableRow>
|
|
) : (
|
|
requests.map((request: any) => (
|
|
<TableRow key={request.requestId}>
|
|
<TableCell>
|
|
<div className="font-medium text-slate-900">{request.requestId}</div>
|
|
<div className="text-slate-600 text-sm">{request.outlet?.code || 'N/A'}</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="font-medium text-slate-900">{request.outlet?.name || 'N/A'}</div>
|
|
<div className="text-slate-600 text-sm">{request.outlet?.city || request.outlet?.address || 'N/A'}</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center gap-2">
|
|
<Badge className={getTypeColor(request.outlet?.type || 'Proprietorship')}>
|
|
{request.outlet?.type || 'Proprietorship'}
|
|
</Badge>
|
|
<ArrowRight className="w-4 h-4 text-slate-400" />
|
|
<Badge className={getTypeColor(request.changeType)}>
|
|
{request.changeType}
|
|
</Badge>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Badge variant="outline" className="border-slate-300 text-slate-700">
|
|
{request.currentStage}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center gap-2">
|
|
<div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full bg-amber-600 transition-all duration-300"
|
|
style={{ width: `${request.progressPercentage || 0}%` }}
|
|
/>
|
|
</div>
|
|
<span className="text-slate-600 text-sm">{request.progressPercentage || 0}%</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="text-slate-900">{new Date(request.createdAt).toLocaleDateString()}</div>
|
|
<div className="text-slate-600 text-sm">By {request.dealer?.fullName || 'Dealer'}</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => onViewDetails(request.requestId)}
|
|
>
|
|
<Eye className="w-4 h-4 mr-1" />
|
|
View
|
|
</Button>
|
|
</TableCell>
|
|
</TableRow>
|
|
))
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="pending" className="mt-4">
|
|
<div className="border border-slate-200 rounded-lg overflow-hidden">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow className="bg-slate-50">
|
|
<TableHead>Request ID</TableHead>
|
|
<TableHead>Dealer Details</TableHead>
|
|
<TableHead>Constitutional Change</TableHead>
|
|
<TableHead>Current Stage</TableHead>
|
|
<TableHead>Status</TableHead>
|
|
<TableHead>Actions</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{requests
|
|
.filter((r: any) => r.status.includes('Review') || r.status.includes('Pending'))
|
|
.map((request: any) => (
|
|
<TableRow key={request.requestId}>
|
|
<TableCell>
|
|
<div className="font-medium text-slate-900">{request.requestId}</div>
|
|
<div className="text-slate-600 text-sm">{request.outlet?.code || 'N/A'}</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="font-medium text-slate-900">{request.outlet?.name || 'N/A'}</div>
|
|
<div className="text-slate-600 text-sm">{request.outlet?.city || 'N/A'}</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center gap-2">
|
|
<Badge className={getTypeColor(request.outlet?.type || 'Proprietorship')}>
|
|
{request.outlet?.type || 'Proprietorship'}
|
|
</Badge>
|
|
<ArrowRight className="w-4 h-4 text-slate-400" />
|
|
<Badge className={getTypeColor(request.changeType)}>
|
|
{request.changeType}
|
|
</Badge>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Badge variant="outline" className="border-slate-300 text-slate-700">
|
|
{request.currentStage}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Badge className={getStatusColor(request.status)}>
|
|
{request.status}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => onViewDetails(request.requestId)}
|
|
>
|
|
<Eye className="w-4 h-4 mr-1" />
|
|
View
|
|
</Button>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
{requests.filter((r: any) => r.status.includes('Review') || r.status.includes('Pending')).length === 0 && (
|
|
<TableRow>
|
|
<TableCell colSpan={6} className="text-center py-8 text-slate-500">
|
|
No pending requests found
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="in-progress" className="mt-4">
|
|
<div className="border border-slate-200 rounded-lg overflow-hidden">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow className="bg-slate-50">
|
|
<TableHead>Request ID</TableHead>
|
|
<TableHead>Dealer Details</TableHead>
|
|
<TableHead>Constitutional Change</TableHead>
|
|
<TableHead>Progress</TableHead>
|
|
<TableHead>Current Stage</TableHead>
|
|
<TableHead>Actions</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{requests
|
|
.filter((r: any) => r.status !== 'Completed' && !r.status.includes('Rejected'))
|
|
.map((request: any) => (
|
|
<TableRow key={request.requestId}>
|
|
<TableCell>
|
|
<div className="font-medium text-slate-900">{request.requestId}</div>
|
|
<div className="text-slate-600 text-sm">{request.outlet?.code || 'N/A'}</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="font-medium text-slate-900">{request.outlet?.name || 'N/A'}</div>
|
|
<div className="text-slate-600 text-sm">{request.outlet?.city || 'N/A'}</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center gap-2">
|
|
<Badge className={getTypeColor(request.outlet?.type || 'Proprietorship')}>
|
|
{request.outlet?.type || 'Proprietorship'}
|
|
</Badge>
|
|
<ArrowRight className="w-4 h-4 text-slate-400" />
|
|
<Badge className={getTypeColor(request.changeType)}>
|
|
{request.changeType}
|
|
</Badge>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center gap-2">
|
|
<div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full bg-amber-600 transition-all duration-300"
|
|
style={{ width: `${request.progressPercentage || 0}%` }}
|
|
/>
|
|
</div>
|
|
<span className="text-slate-600 text-sm">{request.progressPercentage || 0}%</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Badge variant="outline" className="border-slate-300 text-slate-700">
|
|
{request.currentStage}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => onViewDetails(request.requestId)}
|
|
>
|
|
<Eye className="w-4 h-4 mr-1" />
|
|
View
|
|
</Button>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
{requests.filter((r: any) => r.status !== 'Completed' && !r.status.includes('Rejected')).length === 0 && (
|
|
<TableRow>
|
|
<TableCell colSpan={6} className="text-center py-8 text-slate-500">
|
|
No in-progress requests found
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="completed" className="mt-4">
|
|
<div className="border border-slate-200 rounded-lg overflow-hidden">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow className="bg-slate-50">
|
|
<TableHead>Request ID</TableHead>
|
|
<TableHead>Dealer Details</TableHead>
|
|
<TableHead>Constitutional Change</TableHead>
|
|
<TableHead>Status</TableHead>
|
|
<TableHead>Submitted On</TableHead>
|
|
<TableHead>Actions</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{requests
|
|
.filter((r: any) => r.status === 'Completed' || r.status === 'Closed')
|
|
.map((request: any) => (
|
|
<TableRow key={request.requestId}>
|
|
<TableCell>
|
|
<div className="font-medium text-slate-900">{request.requestId}</div>
|
|
<div className="text-slate-600 text-sm">{request.outlet?.code || 'N/A'}</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="font-medium text-slate-900">{request.outlet?.name || 'N/A'}</div>
|
|
<div className="text-slate-600 text-sm">{request.outlet?.city || 'N/A'}</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center gap-2">
|
|
<Badge className={getTypeColor(request.outlet?.type || 'Proprietorship')}>
|
|
{request.outlet?.type || 'Proprietorship'}
|
|
</Badge>
|
|
<ArrowRight className="w-4 h-4 text-slate-400" />
|
|
<Badge className={getTypeColor(request.changeType)}>
|
|
{request.changeType}
|
|
</Badge>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Badge className={getStatusColor(request.status)}>
|
|
{request.status}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="text-slate-900">{new Date(request.createdAt).toLocaleDateString()}</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => onViewDetails(request.requestId)}
|
|
>
|
|
<Eye className="w-4 h-4 mr-1" />
|
|
View
|
|
</Button>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
{requests.filter((r: any) => r.status === 'Completed' || r.status === 'Closed').length === 0 && (
|
|
<TableRow>
|
|
<TableCell colSpan={6} className="text-center py-8 text-slate-500">
|
|
No completed requests found
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|