Fix the frontend API calls and implement logins on frontend #7

Merged
dylan merged 24 commits from feat/update-frontend-api-calls into main 2026-03-04 20:20:50 +00:00
7 changed files with 98 additions and 34 deletions
Showing only changes of commit 531ddb0467 - Show all commits

View File

@@ -21,7 +21,7 @@ function App() {
<Route path="/login" element={<LoginPage />} /> <Route path="/login" element={<LoginPage />} />
<Route path="/upload" element={<UploadPage />} /> <Route path="/upload" element={<UploadPage />} />
<Route path="/dataset/:datasetId/status" element={<DatasetStatusPage />} /> <Route path="/dataset/:datasetId/status" element={<DatasetStatusPage />} />
<Route path="/stats" element={<StatPage />} /> <Route path="/dataset/:datasetId/stats" element={<StatPage />} />
</Route> </Route>
</Routes> </Routes>
); );

View File

@@ -3,12 +3,13 @@ import axios from "axios";
import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { Outlet, useLocation, useNavigate } from "react-router-dom";
import StatsStyling from "../styles/stats_styling"; import StatsStyling from "../styles/stats_styling";
const API_BASE_URL = import.meta.env.VITE_BACKEND_URL
type ProfileResponse = { type ProfileResponse = {
user?: Record<string, unknown>; user?: Record<string, unknown>;
}; };
const styles = StatsStyling; const styles = StatsStyling;
const API_BASE_URL = "http://localhost:5000";
const getUserLabel = (user: Record<string, unknown> | null) => { const getUserLabel = (user: Record<string, unknown> | null) => {
if (!user) { if (!user) {

View File

@@ -3,6 +3,8 @@ import axios from "axios";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import StatsStyling from "../styles/stats_styling"; import StatsStyling from "../styles/stats_styling";
const API_BASE_URL = import.meta.env.VITE_BACKEND_URL
type DatasetStatusResponse = { type DatasetStatusResponse = {
status?: "processing" | "complete" | "error"; status?: "processing" | "complete" | "error";
status_message?: string | null; status_message?: string | null;
@@ -10,7 +12,6 @@ type DatasetStatusResponse = {
}; };
const styles = StatsStyling; const styles = StatsStyling;
const API_BASE_URL = "http://localhost:5000";
const DatasetStatusPage = () => { const DatasetStatusPage = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -43,7 +44,7 @@ const DatasetStatusPage = () => {
if (nextStatus === "complete") { if (nextStatus === "complete") {
window.setTimeout(() => { window.setTimeout(() => {
navigate("/stats", { replace: true }); navigate(`/dataset/${parsedDatasetId}/stats`, { replace: true });
}, 800); }, 800);
} }
} catch (error: unknown) { } catch (error: unknown) {

View File

@@ -3,8 +3,9 @@ import axios from "axios";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import StatsStyling from "../styles/stats_styling"; import StatsStyling from "../styles/stats_styling";
const API_BASE_URL = import.meta.env.VITE_BACKEND_URL
const styles = StatsStyling; const styles = StatsStyling;
const API_BASE_URL = "http://localhost:5000";
const controlStyle = { const controlStyle = {
width: "100%", width: "100%",
maxWidth: "100%", maxWidth: "100%",

View File

@@ -1,5 +1,6 @@
import { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import axios from "axios"; import axios from "axios";
import { useParams } from "react-router-dom";
import StatsStyling from "../styles/stats_styling"; import StatsStyling from "../styles/stats_styling";
import SummaryStats from "../components/SummaryStats"; import SummaryStats from "../components/SummaryStats";
import EmotionalStats from "../components/EmotionalStats"; import EmotionalStats from "../components/EmotionalStats";
@@ -12,9 +13,11 @@ import {
type ContentAnalysisResponse type ContentAnalysisResponse
} from '../types/ApiTypes' } from '../types/ApiTypes'
const API_BASE_URL = import.meta.env.VITE_BACKEND_URL
const styles = StatsStyling; const styles = StatsStyling;
const StatPage = () => { const StatPage = () => {
const { datasetId: routeDatasetId } = useParams<{ datasetId: string }>();
const [error, setError] = useState(''); const [error, setError] = useState('');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [activeView, setActiveView] = useState<"summary" | "emotional" | "interaction">("summary"); const [activeView, setActiveView] = useState<"summary" | "emotional" | "interaction">("summary");
@@ -29,15 +32,73 @@ const StatPage = () => {
const beforeDateRef = useRef<HTMLInputElement>(null); const beforeDateRef = useRef<HTMLInputElement>(null);
const afterDateRef = useRef<HTMLInputElement>(null); const afterDateRef = useRef<HTMLInputElement>(null);
const getStats = () => { const parsedDatasetId = Number(routeDatasetId ?? "");
const datasetId = Number.isInteger(parsedDatasetId) && parsedDatasetId > 0 ? parsedDatasetId : null;
const getFilterParams = () => {
const params: Record<string, string> = {};
const query = (searchInputRef.current?.value ?? "").trim();
const start = (afterDateRef.current?.value ?? "").trim();
const end = (beforeDateRef.current?.value ?? "").trim();
if (query) {
params.search_query = query;
}
if (start) {
params.start_date = start;
}
if (end) {
params.end_date = end;
}
return params;
};
const getAuthHeaders = () => {
const token = localStorage.getItem("access_token");
if (!token) {
return null;
}
return {
Authorization: `Bearer ${token}`,
};
};
const getStats = (params: Record<string, string> = {}) => {
if (!datasetId) {
setError("Missing dataset id. Open /dataset/<id>/stats.");
return;
}
const authHeaders = getAuthHeaders();
if (!authHeaders) {
setError("You must be signed in to load stats.");
return;
}
setError(""); setError("");
setLoading(true); setLoading(true);
Promise.all([ Promise.all([
axios.get<TimeAnalysisResponse>("http://localhost:5000/stats/time"), axios.get<TimeAnalysisResponse>(`${API_BASE_URL}/dataset/${datasetId}/time`, {
axios.get<UserAnalysisResponse>("http://localhost:5000/stats/user"), params,
axios.get<ContentAnalysisResponse>("http://localhost:5000/stats/content"), headers: authHeaders,
axios.get<SummaryResponse>(`http://localhost:5000/stats/summary`), }),
axios.get<UserAnalysisResponse>(`${API_BASE_URL}/dataset/${datasetId}/user`, {
params,
headers: authHeaders,
}),
axios.get<ContentAnalysisResponse>(`${API_BASE_URL}/dataset/${datasetId}/content`, {
params,
headers: authHeaders,
}),
axios.get<SummaryResponse>(`${API_BASE_URL}/dataset/${datasetId}/summary`, {
params,
headers: authHeaders,
}),
]) ])
.then(([timeRes, userRes, contentRes, summaryRes]) => { .then(([timeRes, userRes, contentRes, summaryRes]) => {
setUserData(userRes.data || null); setUserData(userRes.data || null);
@@ -50,35 +111,30 @@ const StatPage = () => {
}; };
const onSubmitFilters = () => { const onSubmitFilters = () => {
const query = searchInputRef.current?.value ?? ""; getStats(getFilterParams());
Promise.all([
axios.post("http://localhost:5000/filter/search", {
query: query
}),
])
.then(() => {
getStats();
})
.catch(e => {
setError("Failed to load filters: " + e.response);
})
}; };
const resetFilters = () => { const resetFilters = () => {
axios.get("http://localhost:5000/filter/reset") if (searchInputRef.current) {
.then(() => { searchInputRef.current.value = "";
}
if (beforeDateRef.current) {
beforeDateRef.current.value = "";
}
if (afterDateRef.current) {
afterDateRef.current.value = "";
}
getStats(); getStats();
})
.catch(e => {
setError(e);
})
}; };
useEffect(() => { useEffect(() => {
setError(""); setError("");
if (!datasetId) {
setError("Missing dataset id. Open /dataset/<id>/stats.");
return;
}
getStats(); getStats();
}, []) }, [datasetId])
if (loading) return <p style={{...styles.page, minWidth: "100vh", minHeight: "100vh"}}>Loading insights</p>; if (loading) return <p style={{...styles.page, minWidth: "100vh", minHeight: "100vh"}}>Loading insights</p>;
if (error) return <p style={{...styles.page}}>{error}</p>; if (error) return <p style={{...styles.page}}>{error}</p>;
@@ -119,6 +175,7 @@ return (
</div> </div>
<div style={{ fontSize: 13, color: "#6b7280" }}>Analytics Dashboard</div> <div style={{ fontSize: 13, color: "#6b7280" }}>Analytics Dashboard</div>
<div style={{ fontSize: 13, color: "#6b7280" }}>Dataset #{datasetId ?? "-"}</div>
</div> </div>
<div style={{ ...styles.container, display: "flex", gap: 8, marginTop: 12 }}> <div style={{ ...styles.container, display: "flex", gap: 8, marginTop: 12 }}>

View File

@@ -4,7 +4,7 @@ import { useNavigate } from "react-router-dom";
import StatsStyling from "../styles/stats_styling"; import StatsStyling from "../styles/stats_styling";
const styles = StatsStyling; const styles = StatsStyling;
const API_BASE_URL = "http://localhost:5000"; const API_BASE_URL = import.meta.env.VITE_BACKEND_URL
const UploadPage = () => { const UploadPage = () => {
const [datasetName, setDatasetName] = useState(""); const [datasetName, setDatasetName] = useState("");

View File

@@ -11,5 +11,9 @@ export const getDocumentTitle = (pathname: string) => {
return "Processing Dataset"; return "Processing Dataset";
} }
if (pathname.includes("stats")) {
return "Ethnography Analysis"
}
return STATIC_TITLES[pathname] ?? DEFAULT_TITLE; return STATIC_TITLES[pathname] ?? DEFAULT_TITLE;
}; };