diff --git a/src/pages/admin/upload/controller_admin_upload.jsx b/src/pages/admin/upload/controller_admin_upload.jsx index b31d3f7..d776bfa 100644 --- a/src/pages/admin/upload/controller_admin_upload.jsx +++ b/src/pages/admin/upload/controller_admin_upload.jsx @@ -31,14 +31,27 @@ export function useUploadController() { const ext = f.name.split(".").pop().toLowerCase(); if (ext === "pdf") { + // try { + // const reader = new FileReader(); + // reader.onload = async (e) => { + // const typedArray = new Uint8Array(e.target.result); + // const pdf = await pdfjsLib.getDocument({ data: typedArray }).promise; + // // setPdfPageCount(pdf.numPages); + // dispatch(setPdfPageCount(pdf.numPages)); + // console.log(`📄 PDF terdeteksi dengan ${pdf.numPages} halaman`); + // }; + // reader.readAsArrayBuffer(f); + // } catch (err) { + // console.error("Gagal membaca PDF:", err); + // } try { const reader = new FileReader(); reader.onload = async (e) => { const typedArray = new Uint8Array(e.target.result); const pdf = await pdfjsLib.getDocument({ data: typedArray }).promise; - // setPdfPageCount(pdf.numPages); dispatch(setPdfPageCount(pdf.numPages)); console.log(`📄 PDF terdeteksi dengan ${pdf.numPages} halaman`); + navigate("/admin/upload/pdf"); // 👈 otomatis pindah ke viewer }; reader.readAsArrayBuffer(f); } catch (err) { diff --git a/src/pages/admin/upload/pdf_viewer/controller_pdf_viewer.jsx b/src/pages/admin/upload/pdf_viewer/controller_pdf_viewer.jsx new file mode 100644 index 0000000..e7ecfef --- /dev/null +++ b/src/pages/admin/upload/pdf_viewer/controller_pdf_viewer.jsx @@ -0,0 +1,92 @@ +import { useDispatch, useSelector } from "react-redux"; +import { useState } from "react"; +import * as pdfjsLib from "pdfjs-dist"; +import { setSelectedPages } from "../../../../store/slices/uploadSlice"; +import { uploadPdf } from "../service_admin_upload"; +import { useNavigate } from "react-router-dom"; + +pdfjsLib.GlobalWorkerOptions.workerSrc = new URL( + "pdfjs-dist/build/pdf.worker.mjs", + import.meta.url +).toString(); + +export function usePdfViewerController() { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const { file } = useSelector((state) => state.upload); + + const [pages, setPages] = useState([]); + const [selectedPages, setSelectedPagesLocal] = useState([]); + const [loading, setLoading] = useState(false); + + // Render PDF menjadi gambar + const loadPdfPages = async (pdfFile) => { + setLoading(true); + try { + const reader = new FileReader(); + reader.onload = async (e) => { + const typedArray = new Uint8Array(e.target.result); + const pdf = await pdfjsLib.getDocument({ data: typedArray }).promise; + const pageImages = []; + const totalPages = pdf.numPages; + + for (let pageNum = 1; pageNum <= totalPages; pageNum++) { + const page = await pdf.getPage(pageNum); + const viewport = page.getViewport({ scale: 1 }); + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + canvas.height = viewport.height; + canvas.width = viewport.width; + await page.render({ canvasContext: ctx, viewport }).promise; + const imageUrl = canvas.toDataURL(); + pageImages.push({ pageNum, imageUrl }); + } + + setPages(pageImages); + setLoading(false); + }; + reader.readAsArrayBuffer(pdfFile); + } catch (err) { + console.error("Gagal render PDF:", err); + } finally { + // setLoading(false); + } + }; + + // Toggle halaman yang dipilih + const toggleSelectPage = (pageNum) => { + let updated = [...selectedPages]; + if (updated.includes(pageNum)) { + updated = updated.filter((p) => p !== pageNum); + } else { + if (updated.length >= 3) return; + updated.push(pageNum); + } + setSelectedPagesLocal(updated); + dispatch(setSelectedPages(updated.join(","))); + }; + + const handleProcessPdf = async () => { + if (selectedPages.length === 0) return; + try { + setLoading(true); + const res = await uploadPdf({ pages: selectedPages }); + console.log("PDF processed:", res); + navigate("/admin/upload/validate"); + } catch (err) { + console.error(err); + } finally { + setLoading(false); + } + }; + + return { + file, + pages, + loading, + selectedPages, + loadPdfPages, + toggleSelectPage, + handleProcessPdf, + }; +} diff --git a/src/pages/admin/upload/pdf_viewer/views_admin_pdf_viewer.jsx b/src/pages/admin/upload/pdf_viewer/views_admin_pdf_viewer.jsx new file mode 100644 index 0000000..9430d43 --- /dev/null +++ b/src/pages/admin/upload/pdf_viewer/views_admin_pdf_viewer.jsx @@ -0,0 +1,93 @@ +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { usePdfViewerController } from "./controller_pdf_viewer"; +import LoadingOverlay from "../../../../components/LoadingOverlay"; +import { motion } from "framer-motion"; + +export default function ViewsAdminPdfViewer() { + const { + file, + pages, + loading, + selectedPages, + toggleSelectPage, + handleProcessPdf, + loadPdfPages, + } = usePdfViewerController(); + const navigate = useNavigate(); + + useEffect(() => { + if (file) { + loadPdfPages(file) + }else{ + navigate("/admin/upload", { replace: true }); + }; + }, [file]); + + return ( +
+ {/* Sidebar kiri */} +
+

Daftar Halaman

+
+ {pages.map((p) => ( + + ))} +
+ +
+

+ Dipilih:{" "} + {selectedPages.length > 0 + ? selectedPages.join(", ") + : "Belum ada halaman"} +

+

+ Maksimal 3 halaman yang dapat dipilih. +

+ + +
+
+ + {/* Konten kanan (viewer) */} +
+ +
+ {pages.map((p) => ( + + {`Halaman +

+ Halaman {p.pageNum} +

+
+ ))} +
+
+
+ ); +} diff --git a/src/pages/admin/upload/views_admin_upload.jsx b/src/pages/admin/upload/views_admin_upload.jsx index f118dc3..f922db0 100644 --- a/src/pages/admin/upload/views_admin_upload.jsx +++ b/src/pages/admin/upload/views_admin_upload.jsx @@ -80,11 +80,11 @@ export default function ViewsAdminUploadStep1() { {/* {!file && ( )} */} - {file && ( + {file && ext!= 'pdf' && (
{/* Info File */}
-

+

📎

{ext === "pdf" && pdfPageCount && ( -

+

File PDF {pdfPageCount} halaman.

)} diff --git a/src/routes/AppRouter.jsx b/src/routes/AppRouter.jsx index 589f87d..d715afe 100644 --- a/src/routes/AppRouter.jsx +++ b/src/routes/AppRouter.jsx @@ -102,6 +102,7 @@ import AdminLayout from "../layouts/AdminLayout"; import ViewsAdminHome from "../pages/admin/home/views_admin_home"; import ViewsAdminUploadStep1 from "../pages/admin/upload/views_admin_upload"; import ViewsAdminUploadValidate from "../pages/admin/upload/views_admin_validate_upload"; +import ViewsAdminPdfViewer from "../pages/admin/upload/pdf_viewer/views_admin_pdf_viewer"; import ViewsAdminUploadSuccess from "../pages/admin/upload/views_admin_success_upload"; import ViewsAdminPublikasi from "../pages/admin/publikasi/views_admin_publikasi"; import ViewsAdminUploadRules from "../pages/admin/upload/rules/views_admin_rules_upload"; @@ -127,6 +128,7 @@ const router = createBrowserRouter( { path: "home", element: }, { path: "upload", element: }, { path: "upload/validate", element: }, + { path: "upload/pdf", element: }, { path: "upload/success", element: }, { path: "upload/rules", element: }, { path: "publikasi", element: },