"use client"; import { useEffect, useMemo, useState } from "react"; import { Package, CheckCircle2, Clock, AlertTriangle, Calendar, Search, Plus, Download, Eye, Pencil, Upload, MoreVertical, X,Trash2, } from "lucide-react"; import { api, apiBase } from "@/lib/api"; import ComplianceDetailModal from "@/components/ComplianceDetailModal"; import jsPDF from "jspdf"; import autoTable from "jspdf-autotable"; function imgSrc(url?: string) { if (!url) return ""; if (url.startsWith("data:") || url.startsWith("http")) return url; return `${apiBase}${url}`; } const TYPES = ["FSSAI License", "Drug License", "GMP Certificate", "ISO Certificate", "EPR Registration", "Factory License", "Import Export Code", "Trademark Registration"]; const STATUS: Record = { complete: "bg-emerald-50 text-emerald-700", pending: "bg-amber-50 text-amber-700", expiring: "bg-amber-50 text-amber-700", expired: "bg-rose-50 text-rose-700", }; export default function Compliance() { const [rows, setRows] = useState([]); const [s, setS] = useState(null); const [products, setProducts] = useState([]); const [filters, setFilters] = useState({ search: "", status_filter: "", compliance_type: "", country: "" }); const [detailId, setDetailId] = useState(null); const [menuFor, setMenuFor] = useState(null); const [form, setForm] = useState(null); // add/edit form state (null = closed) const [page, setPage] = useState(1); const pageSize = 25; const [showAlerts, setShowAlerts] = useState(true); const [deleteRow, setDeleteRow] = useState(null); const [deleting, setDeleting] = useState(false); const [selectedRows, setSelectedRows] = useState([]); const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false); function load() { const qs = new URLSearchParams( Object.entries(filters).filter(([, v]) => v) as any ).toString(); api(`/compliance${qs ? "?" + qs : ""}`) .then(setRows) .catch(() => setRows([])); api("/compliance/summary") .then((data) => { setS(data); setShowAlerts(true); }) .catch(() => {}); } useEffect(() => { load(); }, [filters]); useEffect(() => { api("/products").then((d) => setProducts(d.items ?? [])).catch(() => {}); }, []); const paged = useMemo(() => rows.slice((page - 1) * pageSize, page * pageSize), [rows, page]); const totalPages = Math.max(1, Math.ceil(rows.length / pageSize)); const set = (k: string, v: string) => { setPage(1); setFilters((f) => ({ ...f, [k]: v })); }; function handleExport() { const exportRows = selectedRows.length > 0 ? rows.filter((r) => selectedRows.includes(r.id)) : rows; const headers = [ "Product", "SKU", "Compliance Type", "License Number", "License Name", "Issuing Authority", "Issue Date", "Expiry Date", "Days Left", "Status", "Country", "State", "Category", "Applicable To", "Manufacturing Plant", "Remarks", "Created By", "Created On", "Last Updated", "Document URL", ]; const csv = [ headers.join(","), ...exportRows.map((r) => [ `"${r.product_name ?? ""}"`, `"${r.sku ?? ""}"`, `"${r.compliance_type ?? ""}"`, `"${r.license_number ?? ""}"`, `"${r.license_name ?? ""}"`, `"${r.issuing_authority ?? ""}"`, `"${r.issue_date ?? ""}"`, `"${r.expiry_date ?? ""}"`, `"${r.days_left == null ? "-" : r.days_left < 0 ? "Expired" : r.days_left === 0 ? "Today" : `${r.days_left} Days` }"`, `"${r.status ?? ""}"`, `"${r.country ?? ""}"`, `"${r.state ?? ""}"`, `"${r.category ?? ""}"`, `"${r.applicable_to ?? ""}"`, `"${r.plant ?? ""}"`, `"${r.remarks ?? ""}"`, `"${r.created_by ?? ""}"`, `"${r.created_at ? new Date(r.created_at).toLocaleString("en-IN") : ""}"`, `"${r.updated_at ? new Date(r.updated_at).toLocaleString("en-IN") : ""}"`, `"${r.document_url ?? ""}"`, ].join(",") ), ].join("\n"); const blob = new Blob([csv], { type: "text/csv;charset=utf-8;", }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = selectedRows.length > 0 ? `selected_compliance_${selectedRows.length}.csv` : `compliance_${new Date().toISOString().slice(0, 10)}.csv`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } async function handleImport( e: React.ChangeEvent ) { const file = e.target.files?.[0]; if (!file) return; const formData = new FormData(); formData.append("file", file); try { await fetch(`${apiBase}/compliance/import`, { method: "POST", body: formData, headers: { Authorization: `Bearer ${localStorage.getItem("vp_token")}`, }, }); load(); alert("Import successful"); } catch { alert("Import failed"); } e.target.value = ""; } async function handlePdfExport() { const exportRows = selectedRows.length > 0 ? rows.filter((r) => selectedRows.includes(r.id)) : rows; const doc = new jsPDF({ orientation: "landscape", unit: "mm", format: "a4", }); // =========================== // Logo // =========================== try { const img = new Image(); img.src = "/logo.png"; await new Promise((resolve) => { img.onload = resolve; img.onerror = resolve; }); doc.addImage(img, "PNG", 14, 10, 18, 18); } catch {} // =========================== // Company Header // =========================== doc.setFont("helvetica", "bold"); doc.setFontSize(18); doc.text("VerifyPack", 38, 18); doc.setFont("helvetica", "normal"); doc.setFontSize(10); doc.setTextColor(100); doc.text("Digital Trust for Every Product", 38, 24); // =========================== // Report Title // =========================== doc.setDrawColor(220); doc.line(14, 32, 283, 32); doc.setTextColor(0); doc.setFont("helvetica", "bold"); doc.setFontSize(15); doc.text("Compliance Report", 14, 40); doc.setFont("helvetica", "normal"); doc.setFontSize(10); doc.text( `Generated On : ${new Date().toLocaleString("en-IN")}`, 14, 47 ); doc.text( `Total Records : ${exportRows.length}`, 250, 47, { align: "right" } ); // =========================== // Table // =========================== autoTable(doc, { startY: 55, head: [[ "Product", "SKU", "Compliance Type", "License No", "Issue Date", "Expiry Date", "Days Left", "Status", "Country", "Authority", ]], body: exportRows.map((r) => [ r.product_name || "", r.sku || "", r.compliance_type || "", r.license_number || "", r.issue_date ? new Date(r.issue_date).toLocaleDateString("en-IN") : "-", r.expiry_date ? new Date(r.expiry_date).toLocaleDateString("en-IN") : "-", r.days_left == null ? "-" : r.days_left < 0 ? "Expired" : r.days_left === 0 ? "Today" : `${r.days_left} Days`, r.days_left == null ? "Pending" : r.days_left < 0 ? "Expired" : r.days_left <= 30 ? "Expiring" : "Complete", r.country || "-", r.issuing_authority || "-", ]), styles: { fontSize: 8, cellPadding: 2, }, headStyles: { fillColor: [37, 99, 235], textColor: 255, fontStyle: "bold", }, alternateRowStyles: { fillColor: [245, 245, 245], }, margin: { left: 14, right: 14, }, didDrawPage: (data) => { doc.setFontSize(9); doc.setTextColor(120); doc.text( `Page ${data.pageNumber}`, doc.internal.pageSize.getWidth() - 20, doc.internal.pageSize.getHeight() - 8, { align: "right", } ); doc.text( "Generated by VerifyPack", 14, doc.internal.pageSize.getHeight() - 8 ); }, }); // =========================== // Save PDF // =========================== doc.save( selectedRows.length ? `selected_compliance_report.pdf` : `compliance_report.pdf` ); } async function confirmDelete() { if (!deleteRow) return; try { setDeleting(true); await api(`/compliance/${deleteRow.id}`, { method: "DELETE", }); setDeleteRow(null); load(); } catch (err) { alert("Failed to delete compliance record."); } finally { setDeleting(false); } } function toggleRow(id: string) { setSelectedRows((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id] ); } const allSelected = paged.length > 0 && paged.every((r) => selectedRows.includes(r.id)); function toggleAll() { if (allSelected) { setSelectedRows((prev) => prev.filter((id) => !paged.some((r) => r.id === id)) ); } else { setSelectedRows((prev) => [ ...new Set([...prev, ...paged.map((r) => r.id)]), ]); } } async function deleteSelected() { try { setDeleting(true); await Promise.all( selectedRows.map((id) => api(`/compliance/${id}`, { method: "DELETE", }) ) ); setSelectedRows([]); setBulkDeleteOpen(false); load(); } finally { setDeleting(false); } } function openAdd() { setForm({ product_id: "", compliance_type: TYPES[0], license_number: "", license_name: "", issuing_authority: "", issue_date: "", expiry_date: "", country: "India", state: "", category: "", applicable_to: "Manufacturing Plant", plant: "", remarks: "", _id: "" }); } function openEdit(r: any) { setDetailId(null); setForm({ ...r, _id: r.id, product_id: r.product_id ?? "", issue_date: r.issue_date ? r.issue_date.slice(0, 10) : "", expiry_date: r.expiry_date ? r.expiry_date.slice(0, 10) : "" }); } async function saveForm() { const { _id, ...payload } = form; if (_id) await api(`/compliance/${_id}`, { method: "PUT", body: JSON.stringify(payload) }); else await api("/compliance", { method: "POST", body: JSON.stringify(payload) }); setForm(null); load(); } return (
setMenuFor(null)}>

Compliance Center

{/* KPIs */}
} tint="bg-blue-50 text-blue-600" /> } tint="bg-emerald-50 text-emerald-600" /> } tint="bg-amber-50 text-amber-600" /> } tint="bg-rose-50 text-rose-500" /> } tint="bg-violet-50 text-violet-600" />
{/* Filters */}
set("search", e.target.value)} placeholder="Search compliance…" className="pl-9 pr-3 py-2 w-52 rounded-lg border border-slate-200 text-sm" />
set("status_filter", v)} opts={["complete", "pending", "expiring", "expired"].map((x) => ({ value: x, label: x }))} all="All Status" /> set("compliance_type", v)} opts={TYPES.map((t) => ({ value: t, label: t }))} all="All Types" /> set("country", v)} opts={[{ value: "India", label: "India" }]} all="All Countries" />
{/* Alerts */} {/* Alerts */} {showAlerts && s?.alerts?.length > 0 && (
Attention needed
{s.alerts.map((a: any, i: number) => (
{a.type} {a.number ? ` (${a.number})` : ""} {" — "} {a.status} {a.expiry ? ` · expires ${new Date(a.expiry).toLocaleDateString()}` : ""}
))}
)}
Total: {rows.length}
{/* Import */} {/* Export */} {/* Add */}
{selectedRows.length > 0 && (
{selectedRows.length} Selected
)}
{/* Table */}
{paged.map((c) => ( ))} {paged.length === 0 && }
Product Type License / Reg. Issue Date Expiry Date Status Country Last Updated Actions
toggleRow(c.id)} />
{c.product_image ? :
}
{c.product_name ?? "—"}
{c.sku ?? ""}
{c.compliance_type} {c.license_number ?? "—"} {c.issue_date ? new Date(c.issue_date).toLocaleDateString() : "—"} {c.expiry_date ? (
{new Date(c.expiry_date).toLocaleDateString()}
{c.days_left != null &&
{c.days_left < 0 ? "(Expired)" : `(In ${c.days_left} days)`}
}
) : "—"}
{c.status} {c.country ?? "—"} {c.updated_at ? (
{new Date(c.updated_at).toLocaleDateString("en-IN")}
{new Date(c.updated_at).toLocaleTimeString("en-IN", { hour: "2-digit", minute: "2-digit", second: "2-digit", })}
) : ( "—" )}
{menuFor === c.id && (
e.stopPropagation()} className="absolute right-4 top-12 z-20 w-44 bg-white rounded-xl border border-slate-200 shadow-lg py-1 text-left">
)}
No compliance records.
Showing {rows.length === 0 ? 0 : (page - 1) * pageSize + 1} to {Math.min(page * pageSize, rows.length)} of {rows.length}
{Array.from({ length: totalPages }).slice(0, 5).map((_, i) => ( ))}
{/* Detail modal */} {detailId && ( setDetailId(null)} onEdit={() => { const r = rows.find((x) => x.id === detailId); if (r) openEdit(r); }} onChanged={load} /> )} {deleteRow && (
setDeleteRow(null)} >
e.stopPropagation()} className="w-full max-w-md rounded-2xl bg-white shadow-xl" >

Delete Compliance

{deleteRow.product_image ? ( ) : (
)}
{deleteRow.product_name}
{deleteRow.compliance_type}
{deleteRow.license_number || "No License Number"}

Are you sure you want to delete this compliance record?

This action cannot be undone.

)} {bulkDeleteOpen && (

Delete Compliance Records

Are you sure you want to delete {" "} {selectedRows.length} compliance record {selectedRows.length > 1 ? "s" : ""} ?

This action cannot be undone.

)} {/* Add/Edit form modal */} {form && (
setForm(null)}>
e.stopPropagation()}>

{form._id ? "Edit Compliance" : "Add Compliance"}

setForm({ ...form, license_number: x })} /> setForm({ ...form, license_name: x })} /> setForm({ ...form, issuing_authority: x })} /> setForm({ ...form, issue_date: x })} /> setForm({ ...form, expiry_date: x })} /> setForm({ ...form, country: x })} /> setForm({ ...form, state: x })} /> setForm({ ...form, category: x })} /> setForm({ ...form, applicable_to: x })} /> setForm({ ...form, plant: x })} /> setForm({ ...form, remarks: x })} />
)}
); } function Kpi({ label, value, sub, subColor = "text-slate-400", icon, tint }: any) { return (
{icon}
{label}
{value}
{sub}
); } function Filter({ label, value, onChange, opts, all }: { label: string; value: string; onChange: (v: string) => void; opts: { value: string; label: string }[]; all: string; }) { return ( ); } function Fld({ label, children, full }: any) { return ; } function Inp({ label, v, on, type = "text", full }: { label: string; v: any; on: (x: string) => void; type?: string; full?: boolean }) { return ( ); }