Re_Figma_Code/src/pages/Form16/components/RequestSubmissionSuccess.tsx
2026-03-18 11:32:48 +05:30

297 lines
12 KiB
TypeScript

import { useEffect } from 'react';
import { motion } from 'framer-motion';
import { CheckCircle2, AlertCircle, Ban } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { StatusChip } from './StatusChip';
import { TimelineStep } from './TimelineStep';
import { contactAdminForForm16Mismatch } from '@/services/form16Api';
import { toast } from 'sonner';
export type SubmissionResultStatus = 'success' | 'mismatch' | 'duplicate' | 'error';
interface RequestSubmissionSuccessProps {
/** 'success' = matched & credit note; 'mismatch' = value mismatch; 'duplicate' = already submitted; 'error' = request received / processing */
status: SubmissionResultStatus;
requestId: string;
creditNoteNumber?: string | null;
/** Optional message (e.g. from API or error) */
message?: string | null;
onComplete: () => void;
onResubmit?: () => void;
/** When status is 'error' (request received), optional handler to open the request detail */
onViewRequest?: () => void;
}
export function RequestSubmissionSuccess({
status,
requestId,
creditNoteNumber,
message,
onComplete,
onResubmit,
onViewRequest,
}: RequestSubmissionSuccessProps) {
const isSuccess = status === 'success';
const isMismatch = status === 'mismatch';
const isMissing26AsMismatch = isMismatch && (message || '').toLowerCase().includes('26as') && (message || '').toLowerCase().includes('no 26as');
const onContactAdmin = async () => {
try {
await contactAdminForForm16Mismatch(requestId);
toast.success('Administrator notified');
} catch (e: any) {
const msg = e?.response?.data?.message || e?.message || 'Failed to notify administrator';
toast.error(String(msg));
}
};
const isDuplicate = status === 'duplicate';
const isError = status === 'error';
useEffect(() => {
if (!isSuccess) return;
const timer = setTimeout(onComplete, 5000);
return () => clearTimeout(timer);
}, [isSuccess, onComplete]);
const steps = [
{ label: 'Form 16A Uploaded', state: isDuplicate ? ('failed' as const) : ('completed' as const) },
{ label: 'Validation', state: isDuplicate ? ('failed' as const) : (isError ? ('pending' as const) : ('completed' as const)) },
{ label: '26AS Matching', state: isSuccess ? ('completed' as const) : (isMismatch || isDuplicate) ? ('failed' as const) : ('pending' as const) },
{ label: 'Credit Note', state: isSuccess ? ('completed' as const) : ('pending' as const) },
];
return (
<div className="min-h-[calc(100vh-10rem)] flex items-center justify-center">
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.4 }}
className="w-full max-w-2xl"
>
<Card
className={
isSuccess
? 'border-teal-200 shadow-xl'
: isDuplicate
? 'border-red-300 shadow-xl bg-red-50/50'
: isMismatch
? 'border-amber-200 shadow-xl bg-amber-50/30'
: 'border-gray-200 shadow-xl bg-gray-50/30'
}
>
<CardContent className="pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.15 }}
className="flex justify-center mb-6"
>
<StatusChip
variant={isSuccess ? 'success' : isDuplicate ? 'failed' : isError ? 'pending' : 'failed'}
label={
isSuccess
? 'Matched & Credit Note Generated'
: isDuplicate
? 'Duplicate Submission'
: isMismatch
? 'Value Mismatch'
: 'Request Received'
}
showIcon={true}
/>
</motion.div>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: 0.2, type: 'spring', stiffness: 200 }}
className="flex justify-center mb-6"
>
<div
className={`w-20 h-20 rounded-full flex items-center justify-center ${
isSuccess ? 'bg-teal-100' : isDuplicate ? 'bg-red-100' : isError ? 'bg-gray-100' : 'bg-amber-100'
}`}
>
{isSuccess ? (
<CheckCircle2 className="w-12 h-12 text-teal-600" />
) : isDuplicate ? (
<Ban className="w-12 h-12 text-red-600" />
) : isError ? (
<AlertCircle className="w-12 h-12 text-gray-500" />
) : (
<AlertCircle className="w-12 h-12 text-amber-600" />
)}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
className="text-center mb-6"
>
{isSuccess ? (
<>
<h2 className="text-gray-900 mb-2">Request Submitted Successfully</h2>
<p className="text-gray-700 font-medium text-teal-800">
Details have matched and credit note is generated.
</p>
<p className="text-gray-600 text-sm mt-1">
Your Form 16A data matched with 26AS records. Credit note has been generated.
</p>
</>
) : isDuplicate ? (
<>
<h2 className="text-gray-900 mb-2 text-red-700 font-semibold">Duplicate Submission</h2>
<p className="text-gray-900 font-medium">
This Form 16A has already been submitted for the same quarter and financial year.
</p>
<p className="text-gray-700 text-sm mt-1">
A credit note may already have been issued. Please check your Closed Requests or Credit Notes.
</p>
{message && (
<div className="mt-3 p-3 bg-red-50 border-2 border-red-200 rounded-md">
<p className="text-sm font-semibold text-gray-900 mb-1">Duplicate submission not allowed</p>
<p className="text-sm text-gray-900 whitespace-pre-wrap">{message}</p>
</div>
)}
</>
) : isMismatch ? (
<>
<h2 className="text-gray-900 mb-2">Value Mismatch</h2>
<p className="text-gray-700 font-medium text-amber-800">
Resubmit the form carefully.
</p>
<p className="text-gray-600 text-sm mt-1">
Form 16A details did not match with 26AS data. Please verify the certificate and
resubmit with correct details.
</p>
{message && (
<div className="mt-3 p-3 bg-amber-50 border border-amber-200 rounded-md">
<p className="text-sm font-semibold text-amber-900 mb-1">Validation Error:</p>
<p className="text-sm text-amber-800 whitespace-pre-wrap">{message}</p>
</div>
)}
{isMissing26AsMismatch && (
<div className="mt-3 p-3 bg-white border border-amber-200 rounded-md">
<p className="text-sm font-semibold text-gray-900 mb-1">
Contact administrator: FORM 26AS does not match FORM 16A.
</p>
<p className="text-sm text-gray-700">
If you have submitted the updated Form 16A but the latest 26AS is not uploaded for this quarter yet, please notify RE so they can upload/update 26AS.
</p>
</div>
)}
</>
) : (
<>
<h2 className="text-gray-900 mb-2">Request Submitted</h2>
<p className="text-gray-700 font-medium text-gray-800">
Your request has been received and is being processed.
</p>
<p className="text-gray-600 text-sm mt-1">
If there was an issue, you can try again or contact support.
</p>
{message && (
<p className="text-sm text-gray-600 mt-2 italic">{message}</p>
)}
</>
)}
</motion.div>
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 }}
className={`rounded-lg p-4 mb-6 text-center ${
isSuccess ? 'bg-teal-50 border border-teal-200' : isDuplicate ? 'bg-red-50 border-2 border-red-200' : 'bg-white border border-amber-200'
}`}
>
<p className={`text-sm mb-1 ${isDuplicate ? 'text-red-700' : 'text-gray-600'}`}>Request ID</p>
<p className={`font-mono tracking-wide ${isDuplicate ? 'text-red-800 font-semibold' : 'text-gray-900'}`}>{requestId || '—'}</p>
{isSuccess && creditNoteNumber && (
<>
<p className="text-sm text-gray-600 mt-3 mb-1">Credit Note Number</p>
<p className="text-gray-900 font-mono font-medium text-teal-700">
{creditNoteNumber}
</p>
</>
)}
</motion.div>
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5 }}
className="mb-8"
>
<p className="text-sm text-gray-600 mb-4 text-center">Process flow</p>
<div className="flex items-center justify-center gap-0">
{steps.map((step, index) => (
<TimelineStep
key={index}
step={index + 1}
label={step.label}
state={step.state}
isLast={index === steps.length - 1}
/>
))}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.6 }}
className="flex flex-col sm:flex-row items-center justify-center gap-3"
>
{isSuccess ? (
<>
<p className="text-sm text-gray-500 mb-2 sm:mb-0 sm:mr-2">
Redirecting to My Requests in a moment...
</p>
<Button onClick={onComplete} variant="outline" className="border-teal-300 text-teal-700">
Back to My Requests
</Button>
</>
) : (
<>
{isError && onViewRequest && (
<Button onClick={onViewRequest} className="bg-teal-600 hover:bg-teal-700">
View Request
</Button>
)}
{onResubmit && (isMismatch || isDuplicate || isError) && (
<Button
onClick={onResubmit}
className={
isDuplicate
? 'bg-red-600 hover:bg-red-700'
: isMismatch
? 'bg-amber-600 hover:bg-amber-700'
: 'bg-gray-600 hover:bg-gray-700'
}
>
{isDuplicate ? 'Back to New Submission' : isMismatch ? 'Resubmit Form 16A' : 'Try Again'}
</Button>
)}
{isMissing26AsMismatch && (
<Button onClick={onContactAdmin} variant="outline" className="border-amber-300 text-amber-800">
Contact admin
</Button>
)}
<Button onClick={onComplete} variant="outline">
Back to My Requests
</Button>
</>
)}
</motion.div>
</CardContent>
</Card>
</motion.div>
</div>
);
}