import React, { useState, useEffect } from "react"; import Sidebar from "./components/Sidebar"; import UploadView from "./components/UploadView"; import DataPreview from "./components/DataPreview"; import QueryView from "./components/QueryView"; import SchemaPreviewPanel from "./components/SchemaPreviewPanel"; import SchemaQueryView from "./components/SchemaQueryView"; import SchemaUploadModal from "./components/SchemaUploadModal"; import { getDatasets, getSchema, getPreview, getQueryHistory, getSchemaDatasets, getSchemaInfo, getSchemaPreview } from "./api"; export default function App() { const [datasets, setDatasets] = useState([]); const [schemaDatasets, setSchemaDatasets] = useState([]); const [selectedDataset, setSelectedDataset] = useState(null); const [view, setView] = useState("upload"); // upload | preview | query const [globalLoading, setGlobalLoading] = useState(false); const [datasetsLoading, setDatasetsLoading] = useState(true); // Added polish features const [history, setHistory] = useState([]); const [queriesRun, setQueriesRun] = useState(0); const [activeQuestion, setActiveQuestion] = useState(null); const [toasts, setToasts] = useState([]); const [isMobile, setIsMobile] = useState(window.innerWidth < 768); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); // 1. Initial Load: Fetch all datasets useEffect(() => { fetchDatasetsList(); }, []); // Resize listener for mobile responsiveness useEffect(() => { const handleResize = () => { setIsMobile(window.innerWidth < 768); }; window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); // Toast Notification manager const addToast = (message, type = "info") => { const id = Date.now(); setToasts((prev) => [...prev, { id, message, type }]); setTimeout(() => { setToasts((prev) => prev.filter((t) => t.id !== id)); }, 3000); }; const fetchDatasetsList = async () => { try { setDatasetsLoading(true); const [csvRes, schemaRes] = await Promise.all([ getDatasets(), getSchemaDatasets().catch(() => ({ datasets: [], total: 0 })) ]); setDatasets(csvRes.datasets || []); setSchemaDatasets(schemaRes.datasets || []); setDatasetsLoading(false); } catch (err) { console.error("Failed to load datasets list:", err); setDatasetsLoading(false); } }; const fetchQueryHistory = async (dbName) => { try { const res = await getQueryHistory(dbName); setHistory(res.history || []); } catch (err) { console.error("Failed to fetch query history:", err); } }; // 2. Select Dataset: Fetch details and preview in parallel const handleSelectDataset = async (datasetInfo) => { setGlobalLoading(true); try { console.log("[App] Selecting dataset:", datasetInfo.db_name, "mode:", datasetInfo.mode); if (datasetInfo.mode === "schema") { const [schemaInfoRes, historyRes] = await Promise.all([ getSchemaInfo(datasetInfo.db_name), getQueryHistory(datasetInfo.db_name).catch(() => ({ history: [] })) ]); const mergedDataset = { db_name: datasetInfo.db_name, display_name: datasetInfo.display_name, description: datasetInfo.description, is_sample: datasetInfo.is_sample, mode: "schema", total_tables: schemaInfoRes.total_tables, total_rows: schemaInfoRes.total_rows, relationships: schemaInfoRes.relationships, tables: schemaInfoRes.tables }; setSelectedDataset(mergedDataset); setHistory(historyRes.history || []); setView("preview"); setGlobalLoading(false); return; } // Existing CSV mode: const [schemaRes, previewRes, historyRes] = await Promise.all([ getSchema(datasetInfo.db_name), getPreview(datasetInfo.db_name), getQueryHistory(datasetInfo.db_name).catch(() => ({ history: [] })) ]); const mergedDataset = { db_name: datasetInfo.db_name, display_name: datasetInfo.display_name, description: datasetInfo.description, is_sample: datasetInfo.is_sample, mode: "csv", row_count: schemaRes.row_count, columns: schemaRes.columns, // columns details list {name, type, sample_values} preview: previewRes // preview data columns and rows }; setSelectedDataset(mergedDataset); setHistory(historyRes.history || []); setView("preview"); setGlobalLoading(false); } catch (err) { console.error("[App] Failed to load dataset details:", err); addToast(`Failed to load dataset details: ${err.message}`, "error"); setGlobalLoading(false); } }; // 3. Upload Success Callback: Refresh datasets and select new database const handleUploadSuccess = async (uploadResult) => { console.log("[App] CSV Upload success callback triggered:", uploadResult); addToast("CSV uploaded successfully", "success"); await fetchDatasetsList(); handleSelectDataset({ db_name: uploadResult.db_name, display_name: uploadResult.display_name, description: uploadResult.message, is_sample: false, mode: "csv" }); }; const handleSchemaUploadSuccess = async (uploadResult) => { console.log("[App] Schema Upload success callback:", uploadResult); await fetchDatasetsList(); const displayFriendlyName = uploadResult.db_name.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase()); handleSelectDataset({ db_name: uploadResult.db_name, display_name: displayFriendlyName, description: uploadResult.message, is_sample: false, mode: "schema" }); }; // 4. Delete Success Callback: Refresh list and redirect view if active const handleDeleteDataset = async (deletedDbName, mode = "csv") => { console.log(`[App] Dataset deleted callback triggered: ${deletedDbName} (mode=${mode})`); addToast("Dataset deleted", "info"); if (selectedDataset && selectedDataset.db_name === deletedDbName) { setSelectedDataset(null); setView("upload"); setHistory([]); } fetchDatasetsList(); }; const handleStartQuerying = () => { setView("query"); }; const handleQuerySuccess = () => { setQueriesRun((prev) => prev + 1); if (selectedDataset) { fetchQueryHistory(selectedDataset.db_name); } }; const handleRunQuestion = (questionText) => { setView("query"); setActiveQuestion({ text: questionText, timestamp: Date.now() }); }; return (