import { useState, useEffect, useCallback, useMemo } from 'react'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Input } from '@/components/ui/input'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Search, Loader2, Receipt, Download, RefreshCw, FileText, IndianRupee, CalendarClock } from 'lucide-react'; import { listCreditNotes, getCreditNoteSapResponse, type Form16CreditNoteItem, type Form16SapResponseRecord, type ListCreditNotesParams, type ListCreditNotesSummary, } from '@/services/form16Api'; import { toast } from 'sonner'; import apiClient from '@/services/authApi'; function formatDate(value: string | null | undefined): string { if (!value) return '–'; try { const d = new Date(value); return Number.isNaN(d.getTime()) ? value : d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' }); } catch { return value; } } function formatAmount(value: number | null | undefined): string { if (value == null) return '–'; return new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR', maximumFractionDigits: 0 }).format(value); } const DEFAULT_SUMMARY: ListCreditNotesSummary = { totalCreditNotes: 0, totalAmount: 0, activeDealersCount: 0, }; type SapResponsePreview = { fileName: string; downloadUrl?: string | null; row: Record; meta: { amountText: string; issuedAtText: string; }; }; function mapSapResponseToPreviewRow(sap: Form16SapResponseRecord): Record { return { TRNS_UNIQ_NO: sap.trnsUniqNo || '', TDS_TRNS_ID: sap.tdsTransId || sap.claimNumber || '', DOC_NO: sap.sapDocumentNumber || '', MSG_TYP: sap.msgTyp || '', MESSAGE: sap.message || '', }; } function formatIssuedAt(value: string | null | undefined): string { if (!value) return '–'; try { const d = new Date(value); return Number.isNaN(d.getTime()) ? value : d.toLocaleString('en-IN', { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch { return value; } } export function Form16CreditNotes() { const [creditNotes, setCreditNotes] = useState([]); const [summary, setSummary] = useState(DEFAULT_SUMMARY); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [previewOpen, setPreviewOpen] = useState(false); const [previewLoading, setPreviewLoading] = useState(false); const [preview, setPreview] = useState(null); const fetchNotes = useCallback(async (params?: ListCreditNotesParams) => { try { setLoading(true); const result = await listCreditNotes(params); setCreditNotes(result.creditNotes); setSummary(result.summary ?? DEFAULT_SUMMARY); } catch (error: unknown) { console.error('[Form16CreditNotes] Failed to fetch:', error); toast.error(error instanceof Error ? error.message : 'Failed to load credit notes'); setCreditNotes([]); setSummary(DEFAULT_SUMMARY); } finally { setLoading(false); } }, []); useEffect(() => { fetchNotes(); }, [fetchNotes]); const openSapPreview = useCallback(async (noteId: number, creditNoteNumber?: string | null) => { setPreviewOpen(true); setPreviewLoading(true); setPreview(null); try { const payload = await getCreditNoteSapResponse(noteId); const fileName = (creditNoteNumber || `credit-note-${noteId}`).trim() + '.csv'; const row = mapSapResponseToPreviewRow(payload.sapResponse); // Enrich with UI meta (amount/date from listing row when available) const note = creditNotes.find((n) => Number(n.id) === Number(noteId)); const amountText = formatAmount(note?.amount ?? null); const issuedAtText = formatIssuedAt(note?.issueDate ?? null); setPreview({ fileName, row, meta: { amountText, issuedAtText }, downloadUrl: payload.url || null }); } catch (e: any) { const msg = e?.response?.status === 409 ? 'The credit note is being generated, wait.' : (e instanceof Error ? e.message : 'Failed to load SAP response'); toast.error(String(msg)); setPreviewOpen(false); } finally { setPreviewLoading(false); } }, [creditNotes]); const downloadPreviewCsv = useCallback(async () => { if (!preview?.downloadUrl) { toast.error('CSV download link not available'); return; } window.open(preview.downloadUrl, '_blank'); }, [preview]); const filteredNotes = useMemo(() => { if (!searchQuery.trim()) return creditNotes; const q = searchQuery.trim().toLowerCase(); return creditNotes.filter( (n) => (n.creditNoteNumber && n.creditNoteNumber.toLowerCase().includes(q)) || (n.dealerName && n.dealerName.toLowerCase().includes(q)) || (n.dealerCode && n.dealerCode.toLowerCase().includes(q)) || (n.submission?.form16aNumber && n.submission.form16aNumber.toLowerCase().includes(q)) ); }, [creditNotes, searchQuery]); return (
{ setPreviewOpen(open); if (!open) setPreview(null); }} > SAP Response {previewLoading ? 'Loading SAP response CSV…' : 'Review SAP response details and download the CSV.'} {previewLoading ? (
Loading SAP response…
) : preview ? (
{/* Card 1 (GREEN): SAP identifiers only */}

SAP Response Identifiers

TRNS_UNIQ_NO

{preview.row['TRNS_UNIQ_NO'] || '–'}

TDS_TRNS_ID

{preview.row['TDS_TRNS_ID'] || '–'}

{/* Card 2: Amount + date + ids */}

Credit Note Details

Amount

{preview.meta.amountText}

Issued Date

{preview.meta.issuedAtText}

DOC_NO

{preview.row['DOC_NO'] || '–'}

MSG_TYP

{preview.row['MSG_TYP'] || '–'}

MESSAGE

{preview.row['MESSAGE'] || '–'}

) : (
No preview available.
)}

Credit Notes

View and manage all issued credit notes

{/* Summary Cards */}
Total Credit Notes Issued {loading ? '...' : summary.totalCreditNotes} Total Credit Amount {loading ? '...' : formatAmount(summary.totalAmount)} Active Dealers {loading ? '...' : summary.activeDealersCount}
{/* All Credit Notes */} All Credit Notes Search by credit note number, dealer name, or Form 16A number
setSearchQuery(e.target.value)} className="pl-9" />
Credit Note No. Dealer Name Form 16A No. Amount Issue Date Status Actions {loading ? (

Loading credit notes...

) : filteredNotes.length === 0 ? (

No credit notes found

{searchQuery.trim() ? 'Try a different search.' : 'Credit notes will appear here once issued.'}

) : ( filteredNotes.map((note) => ( {note.creditNoteNumber ? note.creditNoteNumber.startsWith('CN') ? note.creditNoteNumber : `CN #${note.creditNoteNumber}` : '–'} {note.dealerName ?? note.dealerCode ?? '–'} {note.submission?.form16aNumber ?? '–'} {formatAmount(note.amount)} {formatDate(note.issueDate)} {note.status ?? '–'}
{note.sapResponseAvailable ? ( ) : ( )}
)) )}
); }