diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b4d053a..a017f87 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,7 @@ import { useEffect } from "react"; import { Navigate, Route, Routes, useLocation } from "react-router-dom"; import AppLayout from "./components/AppLayout"; +import DatasetsPage from "./pages/Datasets"; import DatasetStatusPage from "./pages/DatasetStatus"; import LoginPage from "./pages/Login"; import UploadPage from "./pages/Upload"; @@ -20,6 +21,7 @@ function App() { } /> } /> } /> + } /> } /> } /> diff --git a/frontend/src/components/AppLayout.tsx b/frontend/src/components/AppLayout.tsx index 2a0c2e0..832515f 100644 --- a/frontend/src/components/AppLayout.tsx +++ b/frontend/src/components/AppLayout.tsx @@ -34,6 +34,9 @@ const AppLayout = () => { const navigate = useNavigate(); const [isSignedIn, setIsSignedIn] = useState(false); const [currentUser, setCurrentUser] = useState | null>(null); + const [lastDatasetId, setLastDatasetId] = useState( + localStorage.getItem("last_dataset_id") + ); const syncAuthState = useCallback(async () => { const token = localStorage.getItem("access_token"); @@ -63,6 +66,17 @@ const AppLayout = () => { void syncAuthState(); }, [location.pathname, syncAuthState]); + useEffect(() => { + const datasetMatch = location.pathname.match(/^\/dataset\/(\d+)\/(status|stats)$/); + if (!datasetMatch) { + return; + } + + const datasetId = datasetMatch[1]; + localStorage.setItem("last_dataset_id", datasetId); + setLastDatasetId(datasetId); + }, [location.pathname]); + const onAuthButtonClick = () => { if (isSignedIn) { localStorage.removeItem("access_token"); @@ -107,6 +121,31 @@ const AppLayout = () => { + navigate("/upload")} + > + Upload + + + navigate("/datasets")} + > + My datasets + + + lastDatasetId && navigate(`/dataset/${lastDatasetId}/stats`)} + disabled={!lastDatasetId} + > + {lastDatasetId ? "Last stats" : "Last stats (none)"} + + { + const navigate = useNavigate(); + const [datasets, setDatasets] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + + useEffect(() => { + const token = localStorage.getItem("access_token"); + if (!token) { + setLoading(false); + setError("You must be signed in to view datasets."); + return; + } + + axios + .get(`${API_BASE_URL}/user/datasets`, { + headers: { Authorization: `Bearer ${token}` }, + }) + .then((response) => { + const sorted = [...(response.data || [])].sort((a, b) => b.id - a.id); + setDatasets(sorted); + }) + .catch((requestError: unknown) => { + if (axios.isAxiosError(requestError)) { + setError(String(requestError.response?.data?.error || requestError.message)); + } else { + setError("Failed to load datasets."); + } + }) + .finally(() => { + setLoading(false); + }); + }, []); + + if (loading) { + return Loading datasets...; + } + + return ( + + + + + My Datasets + + View and reopen datasets you previously uploaded. + + + navigate("/upload")}> + Upload New Dataset + + + + {error && ( + + {error} + + )} + + {!error && datasets.length === 0 && ( + + No datasets yet. Upload one to get started. + + )} + + {!error && datasets.length > 0 && ( + + + {datasets.map((dataset) => { + const isComplete = dataset.status === "complete"; + const targetPath = isComplete + ? `/dataset/${dataset.id}/stats` + : `/dataset/${dataset.id}/status`; + + return ( + + + + {dataset.name || `Dataset #${dataset.id}`} + + + ID #{dataset.id} • Status: {dataset.status || "unknown"} + + {dataset.status_message && ( + + {dataset.status_message} + + )} + + + navigate(targetPath)} + > + {isComplete ? "Open stats" : "View status"} + + + ); + })} + + + )} + + + ); +}; + +export default DatasetsPage; diff --git a/frontend/src/utils/documentTitle.ts b/frontend/src/utils/documentTitle.ts index 779d9b2..904a6a8 100644 --- a/frontend/src/utils/documentTitle.ts +++ b/frontend/src/utils/documentTitle.ts @@ -3,7 +3,7 @@ const DEFAULT_TITLE = "Ethnograph View"; const STATIC_TITLES: Record = { "/login": "Sign In", "/upload": "Upload Dataset", - "/stats": "Stats", + "/datasets": "My Datasets", }; export const getDocumentTitle = (pathname: string) => {
Loading datasets...
+ View and reopen datasets you previously uploaded. +