"use client"; import React, { useState, useMemo } from "react"; import Link from "next/link"; import Markdown from "./Markdown"; import { extractDocCategory, extractDocPoints, extractDocDifficulty, extractDocSolved, sortExportDocuments, compileExportMarkdown, type Document, type ExportData, type ExportSortOption, type ExportCategorySummary, } from "@mcpedia/core/export-helpers"; import { getSectionMeta } from "@mcpedia/config/sections"; interface PdfExportViewProps { exportData: ExportData; backHref: string; } export default function PdfExportView({ exportData, backHref, }: PdfExportViewProps) { const { summary, documents: initialDocs } = exportData; // Configuration and interactive state const [sortBy, setSortBy] = useState("category_points"); const [showCover, setShowCover] = useState(true); const [showScoreboard, setShowScoreboard] = useState(initialDocs.length > 1); const [showTOC, setShowTOC] = useState(initialDocs.length > 1); const [pageBreaks, setPageBreaks] = useState(true); const [selectedCategory, setSelectedCategory] = useState("all"); const [copiedMd, setCopiedMd] = useState(false); const sectionMeta = getSectionMeta(summary.section); const categoriesList = summary.categories || []; const authorsList = summary.authors || []; // Available categories for filtering const allCategories = useMemo(() => { const set = new Set(); for (const doc of initialDocs) { set.add(extractDocCategory(doc)); } return Array.from(set).sort(); }, [initialDocs]); // Filtered & Sorted documents const filteredAndSortedDocs = useMemo(() => { let docs = [...initialDocs]; if (selectedCategory !== "all") { docs = docs.filter( (d) => extractDocCategory(d).toLowerCase() === selectedCategory.toLowerCase(), ); } return sortExportDocuments(docs, sortBy); }, [initialDocs, selectedCategory, sortBy]); // Calculate dynamic stats for currently filtered view const currentTotalPoints = useMemo(() => { return filteredAndSortedDocs.reduce( (sum, d) => sum + extractDocPoints(d), 0, ); }, [filteredAndSortedDocs]); function handlePrint() { window.print(); } function handleCopyMarkdown() { const md = compileExportMarkdown({ summary, documents: filteredAndSortedDocs, }); navigator.clipboard.writeText(md); setCopiedMd(true); setTimeout(() => setCopiedMd(false), 2000); } function handleDownloadMarkdown() { const md = compileExportMarkdown({ summary, documents: filteredAndSortedDocs, }); const blob = new Blob([md], { type: "text/markdown;charset=utf-8" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; const safeName = summary.title .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-|-$/g, ""); a.download = `${safeName}.md`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } return (
{/* ========================================================================= Export Control Toolbar (Screen Only — Hidden in Print) ========================================================================= */} {/* ========================================================================= Formal Printable Document Structure ========================================================================= */}
{/* ======================================================================= 1. Formal Cover Page ======================================================================= */} {showCover && (
{/* Formal Header Line */}
Technical Writeups & Solutions Report Scope: {summary.scope}
{/* Title & Subtitle */}

{summary.title}

Official compilation of challenge solutions, vulnerability analysis, and exploitation procedures.

{/* Summary Information Table */}
{currentTotalPoints > 0 && ( )}
Event / Document Scope {summary.scope}
Total Challenges Compiled {filteredAndSortedDocs.length} challenge{filteredAndSortedDocs.length !== 1 ? "s" : ""}
Total Score / Points {currentTotalPoints.toLocaleString()} pts
Categories Covered {categoriesList.length > 0 ? categoriesList .map((c) => `${c.name} (${c.count})`) .join(", ") : "General"}
Author(s) / Contributors {authorsList.length > 0 ? authorsList.map((a) => `@${a}`).join(", ") : "Contributors"}
Date of Compilation {new Date(summary.generatedAt).toLocaleDateString(undefined, { dateStyle: "long", })}
{/* Cover Footer */}
CONFIDENTIAL & TECHNICAL REPORT {new Date(summary.generatedAt).toLocaleDateString(undefined, { dateStyle: "long", })}
)} {/* ======================================================================= 2. Scoreboard / Challenge Matrix Table ======================================================================= */} {showScoreboard && filteredAndSortedDocs.length > 1 && (

1. Challenge Overview & Scoreboard Matrix

{filteredAndSortedDocs.length} Total Challenges
{filteredAndSortedDocs.map((doc, idx) => { const chNum = idx + 1; const cat = extractDocCategory(doc); const pts = extractDocPoints(doc); const diff = extractDocDifficulty(doc); const isSolved = extractDocSolved(doc); return ( ); })}
# Challenge Category Difficulty Points Author Status
{chNum} {doc.title} {cat} {diff || "-"} {pts > 0 ? `${pts} pts` : "-"} {doc.author ? `@${doc.author}` : "-"} {isSolved ? "Solved" : "-"}
)} {/* ======================================================================= 3. Table of Contents (TOC) ======================================================================= */} {showTOC && filteredAndSortedDocs.length > 1 && (

2. Table of Contents

{filteredAndSortedDocs.map((doc, idx) => { const chNum = idx + 1; const cat = extractDocCategory(doc); const pts = extractDocPoints(doc); return (
Chapter {chNum}: {doc.title}
[{cat}] {pts > 0 && {pts} pts}
); })}
)} {/* ======================================================================= 4. Challenge Chapters ("Urutan Rapih Per Ch") ======================================================================= */}
{filteredAndSortedDocs.map((doc, idx) => { const chNum = idx + 1; const cat = extractDocCategory(doc); const pts = extractDocPoints(doc); const diff = extractDocDifficulty(doc); const isSolved = extractDocSolved(doc); const extra = doc.extraFields || {}; return (
0 ? "page-break-before" : "" } pt-8 first:pt-0 print:pt-6`} > {/* Formal Chapter Header */}
{/* Chapter index label */}
CHAPTER {chNum} · CHALLENGE {chNum} OF {filteredAndSortedDocs.length} {doc.slug}
{/* Challenge Title */}

{doc.title}

{/* Formal Metadata Line */}
Category: {cat} {pts > 0 && ( Points: {pts} pts )} {diff && ( Difficulty: {diff} )} {isSolved && ( Status: Solved )} {doc.author && ( Author: @{doc.author} )} {doc.updatedAt && ( Date:{" "} {new Date(doc.updatedAt).toLocaleDateString()} )} {/* Extra fields */} {Object.entries(extra).map(([k, v]) => { const lowerKey = k.toLowerCase(); if ( [ "category", "points", "score", "pts", "difficulty", "solved", "author", "event", ].includes(lowerKey) || v === null || v === undefined || v === "" ) { return null; } return ( {k}: {String(v)} ); })}
{/* Markdown Writeup Body */}
{/* Chapter End Divider (screen only) */}
); })}
); }