import * as XLSX from 'xlsx'; import { useCallback, useMemo, useRef, useState } from 'react'; export function useExcelParse({ batchSize = 10, processBatch, params }) { const importIdRef = useRef(0); const [importer, setImporter] = useState({ state: 'idle' }); const reset = useCallback(() => { setImporter({ state: 'idle' }); importIdRef.current += 1; }, []); const parseExcel = useCallback(async (file) => { if (!file) return; setImporter({ state: 'parsing' }); const importId = importIdRef.current; try { const buf = await file.arrayBuffer(); const wb = XLSX.read(buf, { type: 'array' }); const sheet = wb.SheetNames[0]; const rows = XLSX.utils.sheet_to_json(wb.Sheets[sheet], { defval: '' }); setImporter({ state: 'running', rowCount: rows.length, importCount: 0, errorCount: 0, remainingTime: null, }); let totalTime = 0; for (let i = 0; i < rows.length; i += batchSize) { if (importIdRef.current !== importId) return; const batch = rows.slice(i, i + batchSize); try { const start = Date.now(); await processBatch(batch, params); totalTime += Date.now() - start; const mean = totalTime / (i + batch.length); setImporter((prev) => prev.state === 'running' ? { ...prev, importCount: prev.importCount + batch.length, remainingTime: mean * (rows.length - (prev.importCount + batch.length)), } : prev ); } catch (err) { setImporter((prev) => prev.state === 'running' ? { ...prev, errorCount: prev.errorCount + batch.length, errorMsg: prev.errorMsg ? `${prev.errorMsg}\n${err?.message || String(err)}` : (err?.message || String(err)), } : prev ); } } setImporter((prev) => prev.state === 'running' ? { ...prev, state: 'complete', remainingTime: null } : prev ); } catch (error) { setImporter({ state: 'error', error }); } }, [batchSize, processBatch, params]); return useMemo(() => ({ importer, parseExcel, reset }), [importer, parseExcel, reset]); }