Dealer_Onboard_Frontend/src/components/dealer/DealerConstitutionalChangePage.tsx

401 lines
16 KiB
TypeScript

import { RefreshCcw, Plus, Eye, Calendar, FileText, Loader2 } from 'lucide-react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card';
import { Badge } from '../ui/badge';
import { Button } from '../ui/button';
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 { dealerService } from '../../services/dealer.service';
interface DealerConstitutionalChangePageProps {
currentUser: UserType | null;
onViewDetails?: (id: string) => void;
}
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';
return 'bg-slate-100 text-slate-700 border-slate-300';
};
const constitutionTypes = ['Proprietorship', 'Partnership', 'LLP', 'Pvt Ltd'];
export function DealerConstitutionalChangePage({ currentUser, onViewDetails }: DealerConstitutionalChangePageProps) {
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [currentConstitution, setCurrentConstitution] = useState('');
const [proposedConstitution, setProposedConstitution] = useState('');
const [reason, setReason] = useState('');
const [newPartners, setNewPartners] = useState('');
const [shareholdingPattern, setShareholdingPattern] = useState('');
const [requests, setRequests] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [profile, setProfile] = useState<any>(null);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
try {
setLoading(true);
const dashboard = await dealerService.getDashboardData();
const constitutionalRes = await dealerService.getConstitutionalChanges();
setProfile(dashboard.profile);
setCurrentConstitution(dashboard.profile?.constitutionType || 'Proprietorship');
setRequests(constitutionalRes.requests || []);
} catch (error) {
console.error('Fetch constitutional data error:', error);
toast.error('Failed to load requests');
} finally {
setLoading(false);
}
};
const handleSubmitRequest = async (e: React.FormEvent) => {
e.preventDefault();
if (!proposedConstitution) {
toast.error('Please select proposed constitution type');
return;
}
if (currentConstitution === proposedConstitution) {
toast.error('Proposed constitution must be different from current');
return;
}
if (!reason.trim()) {
toast.error('Please provide a reason for constitutional change');
return;
}
try {
setSubmitting(true);
const payload = {
currentConstitution,
changeType: proposedConstitution,
reason,
newPartnersDetails: newPartners,
shareholdingPattern
};
await dealerService.submitConstitutionalChange(payload);
toast.success('Constitutional change request submitted successfully');
setIsDialogOpen(false);
fetchData(); // Refresh list
// Reset form
setProposedConstitution('');
setReason('');
setNewPartners('');
setShareholdingPattern('');
} catch (error) {
console.error('Submit constitutional change error:', error);
toast.error('Failed to submit constitutional change request');
} finally {
setSubmitting(false);
}
};
const stats = [
{
title: 'Total Requests',
value: requests.length,
icon: RefreshCcw,
color: 'bg-blue-500',
},
{
title: 'Pending',
value: requests.filter(r => r.status !== 'Completed' && r.status !== 'Rejected').length,
icon: Calendar,
color: 'bg-yellow-500',
},
{
title: 'Completed',
value: requests.filter(r => r.status === 'Completed').length,
icon: FileText,
color: 'bg-green-500',
},
];
return (
<div className="space-y-6">
{/* Loading Overlay */}
{loading && (
<div className="min-h-[400px] flex items-center justify-center">
<Loader2 className="w-8 h-8 text-blue-600 animate-spin" />
</div>
)}
{!loading && (
<>
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-slate-900 mb-2">My Constitutional Change Requests</h1>
<p className="text-slate-600">
Submit and track requests for changing your business constitution
</p>
</div>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button className="bg-blue-600 hover:bg-blue-700">
<Plus className="w-4 h-4 mr-2" />
New Constitutional Change
</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>Submit Constitutional Change Request</DialogTitle>
<DialogDescription>
Request to change your dealership's business constitution structure
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmitRequest} className="space-y-4">
{/* Dealer Info */}
<div className="bg-slate-50 border border-slate-200 rounded-lg p-4 space-y-2">
<h3 className="text-slate-900">Current Dealership Information</h3>
<div className="grid grid-cols-2 gap-3 text-sm">
<div>
<span className="text-slate-600">Dealer Code:</span>
<p className="text-slate-900">{profile?.dealerCode || 'N/A'}</p>
</div>
<div>
<span className="text-slate-600">Dealer Name:</span>
<p className="text-slate-900">{profile?.businessName || 'N/A'}</p>
</div>
<div className="col-span-2">
<span className="text-slate-600">Current Constitution:</span>
<p className="text-slate-900">{currentConstitution}</p>
</div>
</div>
</div>
{/* Constitution Change */}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="currentConstitution">Current Constitution *</Label>
<Input
id="currentConstitution"
value={currentConstitution}
disabled
className="bg-slate-100"
/>
</div>
<div className="space-y-2">
<Label htmlFor="proposedConstitution">Proposed Constitution *</Label>
<Select value={proposedConstitution} onValueChange={setProposedConstitution} required>
<SelectTrigger>
<SelectValue placeholder="Select new constitution" />
</SelectTrigger>
<SelectContent>
{constitutionTypes
.filter(type => type !== currentConstitution)
.map(type => (
<SelectItem key={type} value={type}>{type}</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
{/* Reason */}
<div className="space-y-2">
<Label htmlFor="reason">Reason for Change *</Label>
<Textarea
id="reason"
placeholder="Please provide detailed reason for constitutional change..."
value={reason}
onChange={(e) => setReason(e.target.value)}
rows={4}
required
/>
</div>
{/* New Partners (if applicable) */}
{(proposedConstitution === 'Partnership' || proposedConstitution === 'LLP') && (
<div className="space-y-2">
<Label htmlFor="newPartners">Details of New Partners/Members</Label>
<Textarea
id="newPartners"
placeholder="Name, relationship, and experience of new partners..."
value={newPartners}
onChange={(e) => setNewPartners(e.target.value)}
rows={3}
/>
</div>
)}
{/* Shareholding Pattern */}
{(proposedConstitution === 'Pvt Ltd' || proposedConstitution === 'LLP') && (
<div className="space-y-2">
<Label htmlFor="shareholdingPattern">Proposed Shareholding Pattern</Label>
<Textarea
id="shareholdingPattern"
placeholder="Details of share distribution among partners/directors..."
value={shareholdingPattern}
onChange={(e) => setShareholdingPattern(e.target.value)}
rows={3}
/>
</div>
)}
{/* Document Requirements */}
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<h4 className="text-blue-900 mb-2">Documents Required (to be uploaded later)</h4>
<ul className="text-blue-800 text-sm space-y-1">
<li>• GST Registration Certificate</li>
<li>• Firm PAN Copy</li>
<li>• Partnership Deed (if applicable)</li>
<li>• LLP Agreement (if applicable)</li>
<li>• Certificate of Incorporation (if applicable)</li>
<li>• MOA & AOA (if applicable)</li>
<li>• Board Resolution</li>
<li>• Aadhaar & PAN of all partners/directors</li>
</ul>
</div>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => setIsDialogOpen(false)}
>
Cancel
</Button>
<Button
type="submit"
className="bg-blue-600 hover:bg-blue-700"
disabled={submitting}
>
{submitting ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Submitting...
</>
) : (
'Submit Request'
)}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</div>
{/* Stats */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{stats.map((stat, index) => {
const Icon = stat.icon;
return (
<Card key={index}>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm">{stat.title}</CardTitle>
<div className={`${stat.color} p-2 rounded-lg`}>
<Icon className="h-4 w-4 text-white" />
</div>
</CardHeader>
<CardContent>
<div className="text-slate-900 text-2xl">{stat.value}</div>
</CardContent>
</Card>
);
})}
</div>
{/* Requests Table */}
<Card>
<CardHeader>
<CardTitle>My Constitutional Change Requests</CardTitle>
<CardDescription>
View and track all your constitutional change requests
</CardDescription>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Request ID</TableHead>
<TableHead>Current</TableHead>
<TableHead>Proposed</TableHead>
<TableHead>Submitted On</TableHead>
<TableHead>Current Status</TableHead>
<TableHead>Progress</TableHead>
<TableHead>Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{requests.length === 0 ? (
<TableRow>
<TableCell colSpan={7} className="text-center py-4 text-slate-500">
No constitutional change requests found
</TableCell>
</TableRow>
) : (
requests.map((request) => (
<TableRow key={request.id}>
<TableCell>
<span className="text-slate-900">{request.requestId}</span>
</TableCell>
<TableCell>
<Badge variant="outline">{request.currentConstitution}</Badge>
</TableCell>
<TableCell>
<Badge className="bg-blue-100 text-blue-700 border-blue-300">
{request.changeType}
</Badge>
</TableCell>
<TableCell className="text-slate-600">
{new Date(request.createdAt).toLocaleDateString()}
</TableCell>
<TableCell>
<Badge className={`border ${getStatusColor(request.status)}`}>
{request.status}
</Badge>
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<div className="flex-1 bg-slate-200 rounded-full h-2 min-w-[60px]">
<div
className="bg-blue-600 h-2 rounded-full"
style={{ width: `${request.progressPercentage || 0}%` }}
/>
</div>
<span className="text-xs text-slate-600">{request.progressPercentage || 0}%</span>
</div>
</TableCell>
<TableCell>
<Button
variant="ghost"
size="sm"
onClick={() => onViewDetails && onViewDetails(request.requestId)}
>
<Eye className="w-4 h-4 mr-1" />
View
</Button>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</CardContent>
</Card>
</>
)}
</div>
);
}