satupeta-main/app/(modules)/admin/mapset-upload/_hooks/use-upload.ts

205 lines
6.0 KiB
TypeScript

"use client";
import { useState } from "react";
import { useUploadContext } from "../_context/upload-context";
import uploadApi from "@/shared/services/map-upload"; // Sesuaikan path ini
import * as XLSX from 'xlsx';
import { toast } from "sonner";
// Helper untuk load PDF.js via CDN (Bypassing Webpack)
const loadPdfJs = async () => {
return new Promise<any>((resolve, reject) => {
if ((window as any).pdfjsLib) {
resolve((window as any).pdfjsLib);
return;
}
const script = document.createElement("script");
script.src = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js";
script.async = true;
script.onload = () => {
const pdfjsLib = (window as any).pdfjsLib;
pdfjsLib.GlobalWorkerOptions.workerSrc =
"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js";
resolve(pdfjsLib);
};
script.onerror = (err) => reject(err);
document.body.appendChild(script);
});
};
export function useUploadLogic() {
const { state, setState, goToStep } = useUploadContext();
const [loading, setLoading] = useState(false);
const handleFileSelect = async (file: File) => {
setState((prev) => ({
...prev,
file,
sheetNames: [],
selectedSheet: null,
pdfPageCount: null,
selectedPages: []
}));
const ext = file.name.split(".").pop()?.toLowerCase();
// A. JIKA FILE EXCEL
if (ext === "xlsx" || ext === "xls") {
try {
const data = await file.arrayBuffer();
const workbook = XLSX.read(data, { type: 'array' });
const sheetNames = workbook.SheetNames;
setState((prev) => ({
...prev,
sheetNames: sheetNames,
selectedSheet: sheetNames.length === 1 ? sheetNames[0] : null
}));
if (sheetNames.length > 1) toast.info("Pilih sheet excel.");
} catch (error) {
console.error(error);
toast.error("Gagal membaca file Excel.");
}
}
// B. JIKA FILE PDF -> LOAD VIA CDN RUNTIME
else if (ext === "pdf") {
try {
const pdfjsLib = await loadPdfJs();
const arrayBuffer = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
setState((prev) => ({
...prev,
pdfPageCount: pdf.numPages
}));
// Opsional: Beri info jika halaman banyak
if (pdf.numPages > 1) {
toast.info(`PDF terdeteksi memiliki ${pdf.numPages} halaman.`);
}
} catch (error) {
console.error("Gagal baca PDF:", error);
toast.error("Gagal membaca file PDF atau memuat library.");
}
}
};
const resetFile = () => {
setState(prev => ({
...prev,
file: null,
fileDesc: "",
sheetNames: [],
selectedSheet: null,
pdfPageCount: null,
selectedPages: []
}));
};
// 3. Logic Upload ke Backend
const handleUploadProcess = async (selectedSheet?: string) => {
if (!state.file) return;
// Validasi Deskripsi
if (!state.fileDesc) {
toast.warning("Mohon isi deskripsi file terlebih dahulu.");
return;
}
// Validasi Excel Sheet
const isExcel = state.file.name.match(/\.(xlsx|xls)$/i);
if (isExcel && state.sheetNames.length > 1 && !selectedSheet) {
toast.warning("Mohon pilih sheet yang akan diproses.");
return;
}
// 🔥 LOGIKA BARU: INTERCEPSI PDF MULTIPAGE
const isPdf = state.file.name.match(/\.(pdf)$/i);
if (isPdf && state.pdfPageCount && state.pdfPageCount > 1) {
// Jangan upload dulu, arahkan ke Step 2 (PDF Viewer)
goToStep("PDF_VIEWER");
toast.info("File memiliki beberapa halaman. Silakan pilih halaman yang akan diproses.");
return; // 🛑 BERHENTI DI SINI
}
// --- PROSES UPLOAD LANGSUNG (Untuk Excel, CSV, atau PDF 1 Halaman) ---
setLoading(true);
try {
const res = await uploadApi.uploadFile(
state.file,
state.selectedPages.length > 0 ? state.selectedPages : null,
selectedSheet || state.selectedSheet || null,
state.fileDesc
);
setState((prev) => ({ ...prev, result: res }));
// Routing Logic setelah response backend (untuk kasus PDF 1 halaman atau file lain)
if (res.file_type === ".pdf" && res.tables && res.tables.length > 1) {
goToStep("TABLE_PICKER");
toast.success("Beberapa tabel terdeteksi. Silakan pilih tabel.");
}
else if (res.file_type === ".pdf" && (!res.tables || res.tables.length === 0)) {
// Fallback jika PDF 1 halaman tapi tidak ada tabel
goToStep("PDF_VIEWER");
toast.info("Tabel tidak terdeteksi otomatis. Silakan pilih area manual.");
}
else if (res.data) {
goToStep("VALIDATE");
toast.success("File berhasil diproses. Silakan validasi data.");
} else {
console.log(res);
toast.warning(res.message)
}
} catch (err: any) {
console.error(err);
toast.error(err.message || "Gagal mengunggah file.");
} finally {
setLoading(false);
}
};
// 4. Logic Upload ke Database
const handleSaveToDatabase = async (payload: any) => {
setLoading(true);
try {
// Panggil API
const res = await uploadApi.saveToDatabase(payload);
// Simpan hasil final ke context
setState(prev => ({ ...prev, validatedData: res }));
// Pindah ke halaman sukses
goToStep("SUCCESS");
} catch (err: any) {
console.error(err);
toast.error(err.message || "Gagal menyimpan data ke database.");
} finally {
setLoading(false);
}
};
return {
...state,
loading,
handleFileSelect,
handleUploadProcess,
handleSaveToDatabase,
resetFile,
setState,
setFileDesc: (desc: string) => setState(prev => ({...prev, fileDesc: desc})),
setSelectedSheet: (sheet: string) => setState(prev => ({...prev, selectedSheet: sheet})),
};
}