import React from "react";
import { Document, Page, Text, View } from "@react-pdf/renderer";
import { sharedStyles } from "../ui/sharedPdfStyles";
interface ThreatsSummaryPdfDocumentProps {
threatsData: any[];
reportDateStr: string;
}
interface TableColumn {
header: string;
width: string;
align?: "center" | "right" | "left";
cell: (row: any) => string | number;
}
function formatIndonesiaTime(dateStr: string | Date): string {
if (!dateStr) return "Never";
try {
const d = new Date(dateStr);
return d.toLocaleString("id-ID", {
timeZone: "Asia/Jakarta",
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}) + " WIB";
} catch (e) {
return String(dateStr);
}
}
function RenderPdfTable({ title, columns, data }: { title: string; columns: TableColumn[]; data: any[] }) {
return (
{title}
{columns.map((col, idx) => (
{col.header}
))}
{data.length === 0 ? (
No Data
) : (
data.map((row, rowIdx) => (
{columns.map((col, colIdx) => (
{col.cell(row)}
))}
))
)}
);
}
export default function ThreatsSummaryPdfDocument({
threatsData,
reportDateStr,
}: ThreatsSummaryPdfDocumentProps) {
const total = threatsData.length;
const criticalHigh = threatsData.filter(
(t) => {
const s = (t.severity || "").toLowerCase();
return s.includes("high") || s.includes("critical");
}
).length;
const medium = threatsData.filter(
(t) => (t.severity || "").toLowerCase().includes("med")
).length;
const lowInfo = total - criticalHigh - medium;
// Group by threat type
const typeCounts: { [key: string]: number } = {};
threatsData.forEach((t) => {
const key = t.threat_type || "Unknown Alert";
typeCounts[key] = (typeCounts[key] || 0) + 1;
});
const topTypes = Object.entries(typeCounts)
.map(([type, count]) => ({ type, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 10);
// Group by source IP
const ipCounts: { [key: string]: number } = {};
threatsData.forEach((t) => {
const key = t.ip_address || "Unknown IP";
ipCounts[key] = (ipCounts[key] || 0) + 1;
});
const topIps = Object.entries(ipCounts)
.map(([ip, count]) => ({ ip, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 10);
// Sort incidents by severity (Critical > High > Medium > Low > Info) for PDF export
const SEVERITY_RANK: Record = {
critical: 0, high: 1, medium: 2, med: 2, low: 3, info: 4,
};
const getSeverityRank = (sev: string): number => {
const s = (sev || "").toLowerCase();
const match = Object.entries(SEVERITY_RANK).find(([k]) => s.includes(k));
return match ? match[1] : 5;
};
const incidents = [...threatsData]
.sort((a, b) => getSeverityRank(a.severity) - getSeverityRank(b.severity))
.slice(0, 50);
return (
{/* Header */}
Deep Package Inspection
Security & Traffic Analytics Dashboard
Threat Intelligence Summary
Generated at: {reportDateStr}
{/* Threat Counters */}
Threat Metrics Summary
CRITICAL / HIGH THREATS
{criticalHigh}
MEDIUM THREATS
{medium}
LOW / INFO THREATS
{lowInfo}
{/* Threat Types & Targets */}
r.type },
{ header: "Incidents", width: "30%", align: "right", cell: (r) => r.count },
]}
/>
r.ip },
{ header: "Incidents", width: "30%", align: "right", cell: (r) => r.count },
]}
/>
{/* Detailed Incidents List */}
r.detected_at ? formatIndonesiaTime(r.detected_at) : "N/A" },
{ header: "Threat Type", width: "25%", cell: (r) => r.threat_type || "Security Alert" },
{ header: "Severity", width: "15%", align: "center", cell: (r) => (r.severity || "Unknown").toUpperCase() },
{ header: "Host IP", width: "15%", cell: (r) => r.ip_address || "—" },
{ header: "Target Destination", width: "20%", cell: (r) => r.dst_ip || r.domain || "—" },
]}
/>
);
}