import React, { useState, useMemo, useEffect, useCallback, lazy, Suspense } from 'react'; import { DatasetManager } from './components/DatasetManager'; import type { Dataset, DatasetMetadata } from './types'; import * as db from './services/dbService'; import * as apiService from './services/apiService'; import { Spinner } from './components/common/Spinner'; const ComparisonTool = lazy(() => import('./components/ComparisonTool').then(module => ({ default: module.ComparisonTool }))); type View = 'manager' | 'comparison'; const App: React.FC = () => { const [datasets, setDatasets] = useState([]); const [selectedDatasetId, setSelectedDatasetId] = useState(null); const [activeDataset, setActiveDataset] = useState(null); const [view, setView] = useState('manager'); const [isLoading, setIsLoading] = useState(true); const [isNavigating, setIsNavigating] = useState(false); const [error, setError] = useState(null); useEffect(() => { const loadInitialData = async () => { try { const localMeta = await db.getAllDatasetMetadata(); let sharedMeta: DatasetMetadata[] = []; try { sharedMeta = await apiService.getSharedDatasetMetadata(); } catch (e) { console.error("Could not load shared datasets, continuing with local.", e); setError("Could not load cloud datasets. The backend service may be unavailable. Local datasets are still accessible."); } const allMeta = [...sharedMeta, ...localMeta]; setDatasets(allMeta); if (allMeta.length > 0) { // Select the most recent dataset by default const sortedMeta = [...allMeta].sort((a, b) => new Date(b.uploadDate).getTime() - new Date(a.uploadDate).getTime()); setSelectedDatasetId(sortedMeta[0].id); } } catch (error) { console.error("Failed to load initial data", error); setError("A critical error occurred while loading local datasets."); } finally { setIsLoading(false); } }; loadInitialData(); }, []); const addDataset = async (newDataset: Dataset) => { await db.addDataset(newDataset); const localMeta = await db.getAllDatasetMetadata(); const sharedMeta = datasets.filter(d => d.isShared); // Keep existing shared meta setDatasets([...sharedMeta, ...localMeta]); setSelectedDatasetId(newDataset.id); }; const deleteDataset = async (id: string) => { await db.deleteDataset(id); setDatasets(prevDatasets => { const newDatasets = prevDatasets.filter(d => d.id !== id); if (selectedDatasetId === id) { const sortedMeta = [...newDatasets].sort((a, b) => new Date(b.uploadDate).getTime() - new Date(a.uploadDate).getTime()); setSelectedDatasetId(sortedMeta.length > 0 ? sortedMeta[0].id : null); } return newDatasets; }); }; const renameDataset = async (id: string, newName: string) => { await db.renameDataset(id, newName); setDatasets(prev => prev.map(d => d.id === id ? { ...d, name: newName } : d)); }; const processedDatasets = useMemo(() => { return datasets.filter(d => d.processingState === 'processed'); }, [datasets]); const getFullDataset = async (id: string): Promise => { const meta = datasets.find(d => d.id === id); if (!meta) return null; if (meta.isShared) { return apiService.getSharedDataset(id); } else { return db.getDataset(id); } }; const handleOpenComparisonTool = useCallback(async () => { if (!selectedDatasetId) return; const selectedMeta = datasets.find(d => d.id === selectedDatasetId); if (!selectedMeta || selectedMeta.processingState !== 'processed') return; setView('comparison'); setActiveDataset(null); setIsNavigating(true); try { const fullDataset = await getFullDataset(selectedDatasetId); if (!fullDataset) { throw new Error(`Failed to load dataset ${selectedDatasetId}.`); } // *** NEW LOGIC *** // If it's a local dataset, ensure it's in the backend's cache before proceeding. if (!fullDataset.isShared) { console.log("Local dataset selected. Ensuring it's cached on the backend..."); await apiService.ensureDatasetInCache(fullDataset); console.log("Backend cache confirmed."); } setActiveDataset(fullDataset); } catch (error) { console.error("Error preparing comparison tool:", error); alert(`Error: Could not load the selected dataset. ${error instanceof Error ? error.message : ''}`); setView('manager'); // Go back on error } finally { setIsNavigating(false); } }, [selectedDatasetId, datasets]); const handleDatasetChange = useCallback(async (newId: string) => { setSelectedDatasetId(newId); setActiveDataset(null); setIsNavigating(true); try { const fullDataset = await getFullDataset(newId); if (!fullDataset) { throw new Error(`Failed to load dataset ${newId}.`); } // Also ensure cache is hydrated when switching datasets inside the tool if (!fullDataset.isShared) { await apiService.ensureDatasetInCache(fullDataset); } setActiveDataset(fullDataset); } catch (error) { console.error(`Error switching dataset to ${newId}:`, error); setActiveDataset(null); } finally { setIsNavigating(false); } }, []); const mainContent = () => { if (isLoading) { return
Loading Datasets...
; } const errorBanner = error ? (

Cloud Connection Error: {error}

) : null; if (view === 'manager') { return ( ); } if (view === 'comparison') { const fallbackUI =
Loading Comparison Tool...
; if (isNavigating || !activeDataset) { return fallbackUI; } return ( { setView('manager'); setActiveDataset(null); }} /> ); } return null; }; return (

Cross-Modal Object Comparison Tool

{mainContent()}
); }; export default App;