import React, { ReactElement, useEffect, useState } from "react"; import { useGetImportStatisticsQuery, useStartIncrementalImportMutation } from "../../graphql/generated"; import { useStore } from "../../store"; import { useShallow } from "zustand/react/shallow"; import { useImportSessionStatus } from "../../hooks/useImportSessionStatus"; /** * Import statistics with card-based layout and progress bar * Updates in real-time via the useImportSessionStatus hook */ export const RealTimeImportStats = (): ReactElement => { const [importError, setImportError] = useState(null); const { getSocket, disconnectSocket, importJobQueue } = useStore( useShallow((state) => ({ getSocket: state.getSocket, disconnectSocket: state.disconnectSocket, importJobQueue: state.importJobQueue, })) ); // Get filesystem statistics (new files vs already imported) const { data: importStats, isLoading, refetch: refetchStats } = useGetImportStatisticsQuery( {}, { refetchOnWindowFocus: false, refetchInterval: false } ); // Get definitive import session status (handles Socket.IO events internally) const importSession = useImportSessionStatus(); const { mutate: startIncrementalImport, isPending: isStartingImport } = useStartIncrementalImportMutation({ onSuccess: (data) => { if (data.startIncrementalImport.success) { importJobQueue.setStatus("running"); setImportError(null); } }, onError: (error: any) => { console.error("Failed to start import:", error); setImportError(error?.message || "Failed to start import. Please try again."); }, }); const stats = importStats?.getImportStatistics?.stats; const hasNewFiles = stats && stats.newFiles > 0; // Refetch filesystem stats when import completes useEffect(() => { if (importSession.isComplete && importSession.status === "completed") { console.log("[RealTimeImportStats] Import completed, refetching filesystem stats"); refetchStats(); importJobQueue.setStatus("drained"); } }, [importSession.isComplete, importSession.status, refetchStats, importJobQueue]); // Listen to filesystem change events to refetch stats useEffect(() => { const socket = getSocket("/"); const handleFilesystemChange = () => { refetchStats(); }; // File system changes that affect import statistics socket.on("LS_FILE_ADDED", handleFilesystemChange); socket.on("LS_FILE_REMOVED", handleFilesystemChange); socket.on("LS_FILE_CHANGED", handleFilesystemChange); socket.on("LS_DIRECTORY_ADDED", handleFilesystemChange); socket.on("LS_DIRECTORY_REMOVED", handleFilesystemChange); socket.on("LS_LIBRARY_STATISTICS", handleFilesystemChange); return () => { socket.off("LS_FILE_ADDED", handleFilesystemChange); socket.off("LS_FILE_REMOVED", handleFilesystemChange); socket.off("LS_FILE_CHANGED", handleFilesystemChange); socket.off("LS_DIRECTORY_ADDED", handleFilesystemChange); socket.off("LS_DIRECTORY_REMOVED", handleFilesystemChange); socket.off("LS_LIBRARY_STATISTICS", handleFilesystemChange); }; }, [getSocket, refetchStats]); const handleStartImport = async () => { setImportError(null); // Check if import is already active using definitive status if (importSession.isActive) { setImportError( `Cannot start import: An import session "${importSession.sessionId}" is already active. Please wait for it to complete.` ); return; } if (importJobQueue.status === "drained") { localStorage.removeItem("sessionId"); disconnectSocket("/"); setTimeout(() => { getSocket("/"); setTimeout(() => { const sessionId = localStorage.getItem("sessionId") || ""; startIncrementalImport({ sessionId }); }, 500); }, 100); } else { const sessionId = localStorage.getItem("sessionId") || ""; startIncrementalImport({ sessionId }); } }; if (isLoading || !stats) { return
Loading...
; } // Determine button text based on whether there are already imported files const isFirstImport = stats.alreadyImported === 0; const buttonText = isFirstImport ? `Start Import (${stats.newFiles} files)` : `Start Incremental Import (${stats.newFiles} new files)`; // Calculate display statistics const displayStats = importSession.isActive && importSession.stats ? { totalFiles: importSession.stats.filesQueued + stats.alreadyImported, filesQueued: importSession.stats.filesQueued, filesSucceeded: importSession.stats.filesSucceeded, } : { totalFiles: stats.totalLocalFiles, filesQueued: stats.newFiles, filesSucceeded: stats.alreadyImported, }; return (
{/* Error Message */} {importError && (

Import Error

{importError}

)} {/* Import Button - only show when there are new files and no active import */} {hasNewFiles && !importSession.isActive && ( )} {/* Active Import Progress Bar */} {importSession.isActive && (
Importing {importSession.stats?.filesSucceeded || 0} / {importSession.stats?.filesQueued || 0}... {Math.round(importSession.progress)}%
)} {/* Stats Cards */}
{/* Files Detected Card */}
{displayStats.totalFiles}
files detected
{/* To Import Card */}
{displayStats.filesQueued}
to import
{/* Already Imported Card */}
{displayStats.filesSucceeded}
already imported
); }; export default RealTimeImportStats;