145 lines
5.4 KiB
TypeScript
145 lines
5.4 KiB
TypeScript
// app/admin/upload/_components/step-2-pdf-viewer.tsx
|
|
"use client";
|
|
|
|
import { useRef } from "react";
|
|
import { usePdfViewer } from "../_hooks/use-pdf-viewer";
|
|
import { Button } from "@/shared/components/ui/button";
|
|
import { Checkbox } from "@/shared/components/ui/checkbox";
|
|
import { Loader2, CheckSquare, Square } from "lucide-react";
|
|
import { motion } from "framer-motion";
|
|
|
|
export default function StepPdfViewer() {
|
|
const {
|
|
pages,
|
|
loading,
|
|
localSelectedPages,
|
|
toggleSelectPage,
|
|
handleProcessPdf,
|
|
// Fitur Baru dari Hook
|
|
handleSelectAll,
|
|
isAllSelected,
|
|
MAX_SELECT
|
|
} = usePdfViewer();
|
|
|
|
// 🔥 REF UNTUK SCROLLING
|
|
const pageRefs = useRef<{ [key: number]: HTMLDivElement | null }>({});
|
|
|
|
// Fungsi Helper Scroll
|
|
const scrollToPage = (pageNum: number) => {
|
|
const element = pageRefs.current[pageNum];
|
|
if (element) {
|
|
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
}
|
|
};
|
|
|
|
const handleSidebarClick = (pageNum: number) => {
|
|
toggleSelectPage(pageNum);
|
|
// Auto scroll hanya jika kita mencentang (opsional: bisa juga scroll saat uncheck jika mau)
|
|
// Disini saya buat scroll terjadi setiap kali klik baris
|
|
scrollToPage(pageNum);
|
|
};
|
|
|
|
return (
|
|
<div className="flex h-full gap-4">
|
|
{/* Sidebar Kiri */}
|
|
<div className="w-64 border-r pr-4 overflow-y-auto">
|
|
<h2 className="font-semibold mb-4">Pilih Halaman</h2>
|
|
<div className="mb-3 pb-3 border-b">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
className="w-full flex justify-between"
|
|
onClick={handleSelectAll}
|
|
disabled={loading || pages.length === 0}
|
|
>
|
|
<span className="flex items-center gap-2">
|
|
{isAllSelected ? <CheckSquare className="w-4 h-4"/> : <Square className="w-4 h-4"/>}
|
|
{isAllSelected ? "Batalkan Semua" : "Pilih Semua"}
|
|
</span>
|
|
</Button>
|
|
<p className="text-xs text-slate-400 mt-2 text-center">
|
|
Maksimal {MAX_SELECT} halaman.
|
|
</p>
|
|
</div>
|
|
<div className="space-y-2 overflow-y-auto flex-1 pr-2">
|
|
{pages.map((p) => (
|
|
<div
|
|
key={p.pageNum}
|
|
className={`flex items-center space-x-3 p-2 rounded cursor-pointer border transition-colors ${
|
|
localSelectedPages.includes(p.pageNum)
|
|
? "bg-blue-50 border-blue-200"
|
|
: "hover:bg-slate-50 border-transparent"
|
|
}`}
|
|
onClick={() => handleSidebarClick(p.pageNum)}
|
|
>
|
|
<Checkbox
|
|
checked={localSelectedPages.includes(p.pageNum)}
|
|
// onCheckedChange sudah dihandle oleh onClick parent div agar area klik lebih luas
|
|
className="pointer-events-none"
|
|
/>
|
|
<span className={`text-sm ${localSelectedPages.includes(p.pageNum) ? "text-blue-700 font-medium" : "text-slate-600"}`}>
|
|
Halaman {p.pageNum}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="mt-4 pt-4 border-t sticky bottom-0 bg-white">
|
|
<Button
|
|
className="w-full"
|
|
onClick={handleProcessPdf}
|
|
disabled={loading || localSelectedPages.length === 0}
|
|
>
|
|
{loading ? <Loader2 className="animate-spin mr-2" /> : null}
|
|
Proses Halaman
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main Content (Preview Images) */}
|
|
<div className="flex-1 overflow-y-auto bg-gray-100 p-4 rounded-lg">
|
|
{loading && pages.length === 0 && (
|
|
<div className="flex justify-center items-center h-full">
|
|
<Loader2 className="animate-spin h-8 w-8 text-blue-500" />
|
|
<span className="ml-2">Merender PDF...</span>
|
|
</div>
|
|
)}
|
|
|
|
<div className="space-y-8">
|
|
{pages.map((p) => (
|
|
<motion.div
|
|
key={p.pageNum}
|
|
// 🔥 SET REF DI SINI
|
|
ref={(el) => { pageRefs.current[p.pageNum] = el; }}
|
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 0.05 * (p.pageNum > 10 ? 10 : p.pageNum) }} // Optimasi delay list panjang
|
|
|
|
// Styling highlight jika terpilih
|
|
className={`bg-white p-2 shadow-sm rounded-lg border-2 transition-all duration-300 ${
|
|
localSelectedPages.includes(p.pageNum)
|
|
? "border-blue-500 ring-4 ring-blue-500/10"
|
|
: "border-transparent hover:border-slate-300"
|
|
}`}
|
|
onClick={() => toggleSelectPage(p.pageNum)} // Klik gambar juga bisa toggle select
|
|
>
|
|
<div className="relative">
|
|
{/* Badge Nomor Halaman di atas gambar */}
|
|
<div className="absolute top-2 left-2 bg-slate-800/80 text-white text-xs px-2 py-1 rounded backdrop-blur-sm z-10">
|
|
Halaman. {p.pageNum}
|
|
</div>
|
|
|
|
<img
|
|
src={p.imageUrl}
|
|
alt={`Page ${p.pageNum}`}
|
|
className="w-full h-auto rounded border border-slate-100"
|
|
loading="lazy"
|
|
/>
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |