Re_Figma_Code/src/components/workflow/ClaimManagementWizard/ClaimManagementWizard.tsx

683 lines
27 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Badge } from '@/components/ui/badge';
import { Progress } from '@/components/ui/progress';
import { Calendar } from '@/components/ui/calendar';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { motion, AnimatePresence } from 'framer-motion';
import {
ArrowLeft,
ArrowRight,
Calendar as CalendarIcon,
Check,
Receipt,
Building,
MapPin,
Clock,
CheckCircle,
Info,
FileText,
} from 'lucide-react';
import { format } from 'date-fns';
import { toast } from 'sonner';
import { getAllDealers as fetchDealersFromAPI, getDealerByCode, type DealerInfo } from '@/services/dealerApi';
interface ClaimManagementWizardProps {
onBack?: () => void;
onSubmit?: (claimData: any) => void;
}
const CLAIM_TYPES = [
'Marketing Activity',
'Promotional Event',
'Dealer Training',
'Infrastructure Development',
'Customer Experience Initiative',
'Service Campaign'
];
const STEP_NAMES = [
'Claim Details',
'Review & Submit'
];
export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizardProps) {
const [currentStep, setCurrentStep] = useState(1);
const [dealers, setDealers] = useState<DealerInfo[]>([]);
const [loadingDealers, setLoadingDealers] = useState(true);
const [formData, setFormData] = useState({
activityName: '',
activityType: '',
dealerCode: '',
dealerName: '',
dealerEmail: '',
dealerPhone: '',
dealerAddress: '',
activityDate: undefined as Date | undefined,
location: '',
requestDescription: '',
periodStartDate: undefined as Date | undefined,
periodEndDate: undefined as Date | undefined,
estimatedBudget: ''
});
const totalSteps = STEP_NAMES.length;
// Fetch dealers from API on component mount
useEffect(() => {
const fetchDealers = async () => {
setLoadingDealers(true);
try {
const fetchedDealers = await fetchDealersFromAPI();
setDealers(fetchedDealers);
} catch (error) {
toast.error('Failed to load dealer list.');
console.error('Error fetching dealers:', error);
} finally {
setLoadingDealers(false);
}
};
fetchDealers();
}, []);
const updateFormData = (field: string, value: any) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const isStepValid = () => {
switch (currentStep) {
case 1:
return formData.activityName &&
formData.activityType &&
formData.dealerCode &&
formData.dealerName &&
formData.activityDate &&
formData.location &&
formData.requestDescription;
case 2:
return true;
default:
return false;
}
};
const nextStep = () => {
if (currentStep < totalSteps && isStepValid()) {
setCurrentStep(currentStep + 1);
}
};
const prevStep = () => {
if (currentStep > 1) {
setCurrentStep(currentStep - 1);
}
};
const handleDealerChange = async (dealerCode: string) => {
const selectedDealer = dealers.find(d => d.dealerCode === dealerCode);
if (selectedDealer) {
updateFormData('dealerCode', dealerCode);
updateFormData('dealerName', selectedDealer.dealerName);
updateFormData('dealerEmail', selectedDealer.email || '');
updateFormData('dealerPhone', selectedDealer.phone || '');
updateFormData('dealerAddress', ''); // Address not available in API response
// Try to fetch full dealer info from API if available
try {
const fullDealerInfo = await getDealerByCode(dealerCode);
if (fullDealerInfo) {
updateFormData('dealerEmail', fullDealerInfo.email || selectedDealer.email || '');
updateFormData('dealerPhone', fullDealerInfo.phone || selectedDealer.phone || '');
}
} catch (error) {
// Ignore error, use basic info from list
console.debug('Could not fetch full dealer info:', error);
}
}
};
const handleSubmit = () => {
const claimData = {
...formData,
templateType: 'claim-management',
submittedAt: new Date().toISOString(),
status: 'pending',
currentStep: 'initiator-review',
workflowSteps: [
{
step: 1,
name: 'Initiator Evaluation',
status: 'pending',
approver: 'Current User (Initiator)',
description: 'Review and confirm all claim details and documents'
},
{
step: 2,
name: 'IO Confirmation',
status: 'waiting',
approver: 'System',
description: 'Automatic IO generation upon initiator approval'
},
{
step: 3,
name: 'Department Lead Approval',
status: 'waiting',
approver: 'Department Lead',
description: 'Budget blocking and final approval'
},
{
step: 4,
name: 'Document Submission',
status: 'waiting',
approver: 'Dealer',
description: 'Dealer submits completion documents'
},
{
step: 5,
name: 'Document Verification',
status: 'waiting',
approver: 'Initiator',
description: 'Verify completion documents'
},
{
step: 6,
name: 'E-Invoice Generation',
status: 'waiting',
approver: 'System',
description: 'Auto-generate e-invoice based on approved amount'
},
{
step: 7,
name: 'Credit Note Issuance',
status: 'waiting',
approver: 'Finance',
description: 'Issue credit note to dealer'
}
]
};
toast.success('Claim Request Created', {
description: 'Your claim management request has been submitted successfully.'
});
if (onSubmit) {
onSubmit(claimData);
}
};
const renderStepContent = () => {
switch (currentStep) {
case 1:
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="space-y-6"
>
<div className="text-center mb-8">
<div className="w-16 h-16 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-2xl flex items-center justify-center mx-auto mb-4">
<Receipt className="w-8 h-8 text-white" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">Claim Details</h2>
<p className="text-gray-600">
Provide comprehensive information about your claim request
</p>
</div>
<div className="max-w-3xl mx-auto space-y-6">
{/* Activity Name and Type */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<Label htmlFor="activityName" className="text-base font-semibold">Activity Name *</Label>
<Input
id="activityName"
placeholder="e.g., Himalayan Adventure Fest 2024"
value={formData.activityName}
onChange={(e) => updateFormData('activityName', e.target.value)}
className="mt-2 h-12"
/>
</div>
<div>
<Label htmlFor="activityType" className="text-base font-semibold">Activity Type *</Label>
<Select value={formData.activityType} onValueChange={(value) => updateFormData('activityType', value)}>
<SelectTrigger className="mt-2 h-12">
<SelectValue placeholder="Select activity type" />
</SelectTrigger>
<SelectContent>
{CLAIM_TYPES.map((type) => (
<SelectItem key={type} value={type}>{type}</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
{/* Dealer Selection */}
<div>
<Label htmlFor="dealer" className="text-base font-semibold">Dealer Code / Dealer Name *</Label>
<Select value={formData.dealerCode} onValueChange={handleDealerChange} disabled={loadingDealers}>
<SelectTrigger className="mt-2 h-12">
<SelectValue placeholder={loadingDealers ? "Loading dealers..." : "Select dealer"}>
{formData.dealerCode && (
<div className="flex items-center gap-2">
<span className="font-mono text-sm">{formData.dealerCode}</span>
<span className="text-gray-400"></span>
<span>{formData.dealerName}</span>
</div>
)}
</SelectValue>
</SelectTrigger>
<SelectContent>
{dealers.length === 0 && !loadingDealers ? (
<div className="p-2 text-sm text-gray-500">No dealers available</div>
) : (
dealers.map((dealer) => (
<SelectItem key={dealer.dealerCode} value={dealer.dealerCode}>
<div className="flex items-center gap-2">
<span className="font-mono text-sm font-semibold">{dealer.dealerCode}</span>
<span className="text-gray-400"></span>
<span>{dealer.dealerName}</span>
</div>
</SelectItem>
))
)}
</SelectContent>
</Select>
{formData.dealerCode && (
<p className="text-sm text-gray-600 mt-2">
Selected: <span className="font-semibold">{formData.dealerName}</span> ({formData.dealerCode})
</p>
)}
</div>
{/* Date and Location */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<Label className="text-base font-semibold">Date *</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-full justify-start text-left mt-2 h-12"
>
<CalendarIcon className="mr-2 h-4 w-4" />
{formData.activityDate ? format(formData.activityDate, 'PPP') : 'Select date'}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={formData.activityDate}
onSelect={(date) => updateFormData('activityDate', date)}
initialFocus
/>
</PopoverContent>
</Popover>
</div>
<div>
<Label htmlFor="location" className="text-base font-semibold">Location *</Label>
<Input
id="location"
placeholder="e.g., Mumbai, Maharashtra"
value={formData.location}
onChange={(e) => updateFormData('location', e.target.value)}
className="mt-2 h-12"
/>
</div>
</div>
{/* Request Detail */}
<div>
<Label htmlFor="requestDescription" className="text-base font-semibold">Request in Detail - Brief Requirement *</Label>
<Textarea
id="requestDescription"
placeholder="Provide a detailed description of your claim requirement..."
value={formData.requestDescription}
onChange={(e) => updateFormData('requestDescription', e.target.value)}
className="mt-2 min-h-[120px]"
/>
<p className="text-xs text-gray-500 mt-1">
Include key details about the claim, objectives, and expected outcomes
</p>
</div>
{/* Period (Optional) */}
<div>
<div className="flex items-center gap-2 mb-3">
<Label className="text-base font-semibold">Period (If Any)</Label>
<Badge variant="secondary" className="text-xs">Optional</Badge>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<Label className="text-sm text-gray-600">Start Date</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-full justify-start text-left mt-2 h-12"
>
<CalendarIcon className="mr-2 h-4 w-4" />
{formData.periodStartDate ? format(formData.periodStartDate, 'PPP') : 'Start date'}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={formData.periodStartDate}
onSelect={(date) => updateFormData('periodStartDate', date)}
initialFocus
/>
</PopoverContent>
</Popover>
</div>
<div>
<Label className="text-sm text-gray-600">End Date</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-full justify-start text-left mt-2 h-12"
>
<CalendarIcon className="mr-2 h-4 w-4" />
{formData.periodEndDate ? format(formData.periodEndDate, 'PPP') : 'End date'}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={formData.periodEndDate}
onSelect={(date) => updateFormData('periodEndDate', date)}
initialFocus
/>
</PopoverContent>
</Popover>
</div>
</div>
{(formData.periodStartDate || formData.periodEndDate) && (
<p className="text-xs text-gray-600 mt-2">
{formData.periodStartDate && formData.periodEndDate
? `Period: ${format(formData.periodStartDate, 'MMM dd, yyyy')} - ${format(formData.periodEndDate, 'MMM dd, yyyy')}`
: 'Please select both start and end dates for the period'}
</p>
)}
</div>
</div>
</motion.div>
);
case 2:
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="space-y-6"
>
<div className="text-center mb-8">
<div className="w-16 h-16 bg-gradient-to-br from-green-500 to-emerald-600 rounded-2xl flex items-center justify-center mx-auto mb-4">
<CheckCircle className="w-8 h-8 text-white" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">Review & Submit</h2>
<p className="text-gray-600">
Review your claim details before submission
</p>
</div>
<div className="max-w-3xl mx-auto space-y-6">
{/* Activity Information */}
<Card className="border-2">
<CardHeader className="bg-gradient-to-br from-blue-50 to-indigo-50">
<CardTitle className="flex items-center gap-2">
<Receipt className="w-5 h-5 text-blue-600" />
Activity Information
</CardTitle>
</CardHeader>
<CardContent className="pt-6 space-y-4">
<div className="grid grid-cols-2 gap-6">
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">Activity Name</Label>
<p className="font-semibold text-gray-900 mt-1">{formData.activityName}</p>
</div>
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">Activity Type</Label>
<Badge variant="secondary" className="mt-1">{formData.activityType}</Badge>
</div>
</div>
</CardContent>
</Card>
{/* Dealer Information */}
<Card className="border-2">
<CardHeader className="bg-gradient-to-br from-green-50 to-emerald-50">
<CardTitle className="flex items-center gap-2">
<Building className="w-5 h-5 text-green-600" />
Dealer Information
</CardTitle>
</CardHeader>
<CardContent className="pt-6 space-y-4">
<div className="grid grid-cols-2 gap-6">
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">Dealer Code</Label>
<p className="font-semibold text-gray-900 mt-1 font-mono">{formData.dealerCode}</p>
</div>
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">Dealer Name</Label>
<p className="font-semibold text-gray-900 mt-1">{formData.dealerName}</p>
</div>
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">Email</Label>
<p className="text-gray-900 mt-1">{formData.dealerEmail}</p>
</div>
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">Phone</Label>
<p className="text-gray-900 mt-1">{formData.dealerPhone}</p>
</div>
{formData.dealerAddress && (
<div className="col-span-2">
<Label className="text-xs text-gray-600 uppercase tracking-wider">Address</Label>
<p className="text-gray-900 mt-1">{formData.dealerAddress}</p>
</div>
)}
</div>
</CardContent>
</Card>
{/* Date & Location */}
<Card className="border-2">
<CardHeader className="bg-gradient-to-br from-purple-50 to-pink-50">
<CardTitle className="flex items-center gap-2">
<CalendarIcon className="w-5 h-5 text-purple-600" />
Date & Location
</CardTitle>
</CardHeader>
<CardContent className="pt-6 space-y-4">
<div className="grid grid-cols-2 gap-6">
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">Date</Label>
<p className="font-semibold text-gray-900 mt-1">
{formData.activityDate ? format(formData.activityDate, 'PPP') : 'N/A'}
</p>
</div>
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">Location</Label>
<div className="flex items-center gap-2 mt-1">
<MapPin className="w-4 h-4 text-gray-500" />
<p className="font-semibold text-gray-900">{formData.location}</p>
</div>
</div>
{formData.estimatedBudget && (
<div className="col-span-2">
<Label className="text-xs text-gray-600 uppercase tracking-wider">Estimated Budget</Label>
<p className="text-xl font-bold text-blue-900 mt-1">{formData.estimatedBudget}</p>
</div>
)}
</div>
</CardContent>
</Card>
{/* Request Details */}
<Card className="border-2">
<CardHeader className="bg-gradient-to-br from-orange-50 to-amber-50">
<CardTitle className="flex items-center gap-2">
<FileText className="w-5 h-5 text-orange-600" />
Request Details
</CardTitle>
</CardHeader>
<CardContent className="pt-6 space-y-4">
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">Brief Requirement</Label>
<div className="mt-2 p-4 bg-gray-50 rounded-lg border">
<p className="text-gray-900 whitespace-pre-wrap">{formData.requestDescription}</p>
</div>
</div>
</CardContent>
</Card>
{/* Period (if provided) */}
{(formData.periodStartDate || formData.periodEndDate) && (
<Card className="border-2">
<CardHeader className="bg-gradient-to-br from-cyan-50 to-blue-50">
<CardTitle className="flex items-center gap-2">
<Clock className="w-5 h-5 text-cyan-600" />
Period
</CardTitle>
</CardHeader>
<CardContent className="pt-6 space-y-4">
<div className="grid grid-cols-2 gap-6">
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">Start Date</Label>
<p className="font-semibold text-gray-900 mt-1">
{formData.periodStartDate ? format(formData.periodStartDate, 'PPP') : 'Not specified'}
</p>
</div>
<div>
<Label className="text-xs text-gray-600 uppercase tracking-wider">End Date</Label>
<p className="font-semibold text-gray-900 mt-1">
{formData.periodEndDate ? format(formData.periodEndDate, 'PPP') : 'Not specified'}
</p>
</div>
</div>
</CardContent>
</Card>
)}
{/* Confirmation Message */}
<div className="bg-blue-50 border-2 border-blue-200 rounded-lg p-6">
<div className="flex items-start gap-3">
<Info className="w-6 h-6 text-blue-600 flex-shrink-0 mt-0.5" />
<div>
<p className="font-semibold text-blue-900 mb-1">Ready to Submit</p>
<p className="text-sm text-blue-800">
Please review all the information above. Once submitted, your claim request will enter the approval workflow.
</p>
</div>
</div>
</div>
</div>
</motion.div>
);
default:
return null;
}
};
return (
<div className="w-full bg-gradient-to-br from-gray-50 to-gray-100 py-4 sm:py-6 lg:py-8 px-3 sm:px-4 lg:px-6 overflow-y-auto">
<div className="max-w-6xl mx-auto pb-8">
{/* Header */}
<div className="mb-6 sm:mb-8">
<Button
variant="ghost"
onClick={onBack}
className="mb-3 sm:mb-4 gap-2 text-sm sm:text-base"
>
<ArrowLeft className="w-3 h-3 sm:w-4 sm:h-4" />
<span className="hidden sm:inline">Back to Templates</span>
<span className="sm:hidden">Back</span>
</Button>
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-0">
<div>
<Badge variant="secondary" className="mb-2 text-xs">Claim Management Template</Badge>
<h1 className="text-xl sm:text-2xl lg:text-3xl font-bold text-gray-900">New Claim Request</h1>
<p className="text-sm sm:text-base text-gray-600 mt-1">
Step {currentStep} of {totalSteps}: <span className="hidden sm:inline">{STEP_NAMES[currentStep - 1]}</span>
</p>
</div>
</div>
{/* Progress Bar */}
<div className="mt-4 sm:mt-6">
<Progress value={(currentStep / totalSteps) * 100} className="h-2" />
<div className="flex justify-between mt-2 px-1">
{STEP_NAMES.map((_name, index) => (
<span
key={index}
className={`text-xs sm:text-sm ${
index + 1 <= currentStep ? 'text-blue-600 font-medium' : 'text-gray-400'
}`}
>
{index + 1}
</span>
))}
</div>
</div>
</div>
{/* Step Content */}
<Card className="mb-6 sm:mb-8">
<CardContent className="p-4 sm:p-6 lg:p-8">
<AnimatePresence mode="wait">
{renderStepContent()}
</AnimatePresence>
</CardContent>
</Card>
{/* Navigation */}
<div className="flex flex-col sm:flex-row justify-between gap-3 sm:gap-0 pb-4 sm:pb-0">
<Button
variant="outline"
onClick={prevStep}
disabled={currentStep === 1}
className="gap-2 w-full sm:w-auto order-2 sm:order-1"
>
<ArrowLeft className="w-4 h-4" />
Previous
</Button>
{currentStep < totalSteps ? (
<Button
onClick={nextStep}
disabled={!isStepValid()}
className="gap-2 w-full sm:w-auto order-1 sm:order-2"
>
Next
<ArrowRight className="w-4 h-4" />
</Button>
) : (
<Button
onClick={handleSubmit}
disabled={!isStepValid()}
className="gap-2 bg-green-600 hover:bg-green-700 w-full sm:w-auto order-1 sm:order-2"
>
<Check className="w-4 h-4" />
Submit Claim Request
</Button>
)}
</div>
</div>
</div>
);
}