204 lines
6.0 KiB
TypeScript
204 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})),
|
|
};
|
|
}
|
|
|
|
|
|
|
|
|
|
|