🔨 Fix for showing accurate import counts

This commit is contained in:
2026-03-09 22:54:47 -04:00
parent 8546641152
commit ba1b5bb965
2 changed files with 62 additions and 13 deletions

View File

@@ -18,6 +18,12 @@ import { useImportSessionStatus } from "../../hooks/useImportSessionStatus";
export const RealTimeImportStats = (): ReactElement => { export const RealTimeImportStats = (): ReactElement => {
const [importError, setImportError] = useState<string | null>(null); const [importError, setImportError] = useState<string | null>(null);
const [detectedFile, setDetectedFile] = useState<string | null>(null); const [detectedFile, setDetectedFile] = useState<string | null>(null);
const [socketImport, setSocketImport] = useState<{
active: boolean;
completed: number;
total: number;
failed: number;
} | null>(null);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { getSocket, disconnectSocket, importJobQueue } = useStore( const { getSocket, disconnectSocket, importJobQueue } = useStore(
@@ -100,14 +106,53 @@ export const RealTimeImportStats = (): ReactElement => {
setTimeout(() => setDetectedFile(null), 5000); setTimeout(() => setDetectedFile(null), 5000);
}; };
const handleImportStarted = () => {
setSocketImport({ active: true, completed: 0, total: 0, failed: 0 });
};
const handleCoverExtracted = (payload: {
completedJobCount: number;
totalJobCount: number;
importResult: unknown;
}) => {
setSocketImport((prev) => ({
active: true,
completed: payload.completedJobCount,
total: payload.totalJobCount,
failed: prev?.failed ?? 0,
}));
};
const handleCoverExtractionFailed = (payload: {
failedJobCount: number;
importResult: unknown;
}) => {
setSocketImport((prev) =>
prev ? { ...prev, failed: payload.failedJobCount } : null,
);
};
const handleQueueDrained = () => {
setSocketImport((prev) => (prev ? { ...prev, active: false } : null));
handleStatsChange();
};
socket.on("LS_LIBRARY_STATS", handleStatsChange); socket.on("LS_LIBRARY_STATS", handleStatsChange);
socket.on("LS_FILES_MISSING", handleStatsChange); socket.on("LS_FILES_MISSING", handleStatsChange);
socket.on("LS_FILE_DETECTED", handleFileDetected); socket.on("LS_FILE_DETECTED", handleFileDetected);
socket.on("LS_INCREMENTAL_IMPORT_STARTED", handleImportStarted);
socket.on("LS_COVER_EXTRACTED", handleCoverExtracted);
socket.on("LS_COVER_EXTRACTION_FAILED", handleCoverExtractionFailed);
socket.on("LS_IMPORT_QUEUE_DRAINED", handleQueueDrained);
return () => { return () => {
socket.off("LS_LIBRARY_STATS", handleStatsChange); socket.off("LS_LIBRARY_STATS", handleStatsChange);
socket.off("LS_FILES_MISSING", handleStatsChange); socket.off("LS_FILES_MISSING", handleStatsChange);
socket.off("LS_FILE_DETECTED", handleFileDetected); socket.off("LS_FILE_DETECTED", handleFileDetected);
socket.off("LS_INCREMENTAL_IMPORT_STARTED", handleImportStarted);
socket.off("LS_COVER_EXTRACTED", handleCoverExtracted);
socket.off("LS_COVER_EXTRACTION_FAILED", handleCoverExtractionFailed);
socket.off("LS_IMPORT_QUEUE_DRAINED", handleQueueDrained);
}; };
}, [getSocket, queryClient]); }, [getSocket, queryClient]);
@@ -151,12 +196,14 @@ export const RealTimeImportStats = (): ReactElement => {
const hasSessionStats = importSession.isActive && sessionStats !== null; const hasSessionStats = importSession.isActive && sessionStats !== null;
const totalFiles = stats.totalLocalFiles; const totalFiles = stats.totalLocalFiles;
const importedCount = hasSessionStats const importedCount = stats.alreadyImported;
? sessionStats!.filesSucceeded
: stats.alreadyImported;
const failedCount = hasSessionStats ? sessionStats!.filesFailed : 0; const failedCount = hasSessionStats ? sessionStats!.filesFailed : 0;
const showProgressBar = importSession.isActive; const showProgressBar = socketImport !== null;
const socketProgressPct =
socketImport && socketImport.total > 0
? Math.round((socketImport.completed / socketImport.total) * 100)
: 0;
const showFailedCard = hasSessionStats && failedCount > 0; const showFailedCard = hasSessionStats && failedCount > 0;
const showMissingCard = missingCount > 0; const showMissingCard = missingCount > 0;
@@ -214,20 +261,20 @@ export const RealTimeImportStats = (): ReactElement => {
<div className="space-y-1.5"> <div className="space-y-1.5">
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
<span className="font-medium text-gray-700 dark:text-gray-300"> <span className="font-medium text-gray-700 dark:text-gray-300">
{importSession.isActive {socketImport!.active
? `Importing ${sessionStats?.filesSucceeded ?? 0} / ${sessionStats?.filesQueued ?? 0}` ? `Importing ${socketImport!.completed} / ${socketImport!.total}`
: `${sessionStats?.filesSucceeded ?? 0} / ${sessionStats?.filesQueued ?? 0} imported`} : `${socketImport!.completed} / ${socketImport!.total} imported`}
</span> </span>
<span className="font-semibold text-gray-900 dark:text-white"> <span className="font-semibold text-gray-900 dark:text-white">
{Math.round(importSession.progress)}% complete {socketProgressPct}% complete
</span> </span>
</div> </div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-3 overflow-hidden"> <div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-3 overflow-hidden">
<div <div
className="bg-blue-600 dark:bg-blue-500 h-3 rounded-full transition-all duration-300 relative" className="bg-blue-600 dark:bg-blue-500 h-3 rounded-full transition-all duration-300 relative"
style={{ width: `${importSession.progress}%` }} style={{ width: `${socketProgressPct}%` }}
> >
{importSession.isActive && ( {socketImport!.active && (
<div className="absolute inset-0 bg-linear-to-r from-transparent via-white/20 to-transparent animate-shimmer" /> <div className="absolute inset-0 bg-linear-to-r from-transparent via-white/20 to-transparent animate-shimmer" />
)} )}
</div> </div>
@@ -243,7 +290,7 @@ export const RealTimeImportStats = (): ReactElement => {
style={{ backgroundColor: "#6b7280" }} style={{ backgroundColor: "#6b7280" }}
> >
<div className="text-4xl font-bold text-white mb-2">{totalFiles}</div> <div className="text-4xl font-bold text-white mb-2">{totalFiles}</div>
<div className="text-sm text-gray-200 font-medium">total files</div> <div className="text-sm text-gray-200 font-medium">in import folder</div>
</div> </div>
{/* Imported */} {/* Imported */}
@@ -255,7 +302,7 @@ export const RealTimeImportStats = (): ReactElement => {
{importedCount} {importedCount}
</div> </div>
<div className="text-sm text-gray-700 font-medium"> <div className="text-sm text-gray-700 font-medium">
{importSession.isActive ? "imported so far" : "imported"} {importSession.isActive ? "imported so far" : "imported in database"}
</div> </div>
</div> </div>

View File

@@ -258,7 +258,7 @@ export const useImportSessionStatus = (): ImportSessionState => {
}; };
const handleSessionStarted = () => { const handleSessionStarted = () => {
console.log("[useImportSessionStatus] IMPORT_SESSION_STARTED event received"); console.log("[useImportSessionStatus] IMPORT_SESSION_STARTED / LS_INCREMENTAL_IMPORT_STARTED event received");
// Reset completion flags when new session starts // Reset completion flags when new session starts
completionEventReceived.current = false; completionEventReceived.current = false;
queueDrainedEventReceived.current = false; queueDrainedEventReceived.current = false;
@@ -275,12 +275,14 @@ export const useImportSessionStatus = (): ImportSessionState => {
socket.on("IMPORT_SESSION_COMPLETED", handleSessionCompleted); socket.on("IMPORT_SESSION_COMPLETED", handleSessionCompleted);
socket.on("LS_IMPORT_QUEUE_DRAINED", handleQueueDrained); socket.on("LS_IMPORT_QUEUE_DRAINED", handleQueueDrained);
socket.on("IMPORT_SESSION_STARTED", handleSessionStarted); socket.on("IMPORT_SESSION_STARTED", handleSessionStarted);
socket.on("LS_INCREMENTAL_IMPORT_STARTED", handleSessionStarted);
socket.on("IMPORT_SESSION_UPDATED", handleSessionUpdated); socket.on("IMPORT_SESSION_UPDATED", handleSessionUpdated);
return () => { return () => {
socket.off("IMPORT_SESSION_COMPLETED", handleSessionCompleted); socket.off("IMPORT_SESSION_COMPLETED", handleSessionCompleted);
socket.off("LS_IMPORT_QUEUE_DRAINED", handleQueueDrained); socket.off("LS_IMPORT_QUEUE_DRAINED", handleQueueDrained);
socket.off("IMPORT_SESSION_STARTED", handleSessionStarted); socket.off("IMPORT_SESSION_STARTED", handleSessionStarted);
socket.off("LS_INCREMENTAL_IMPORT_STARTED", handleSessionStarted);
socket.off("IMPORT_SESSION_UPDATED", handleSessionUpdated); socket.off("IMPORT_SESSION_UPDATED", handleSessionUpdated);
}; };
}, [getSocket, refetch]); }, [getSocket, refetch]);