From 2585734f6a42286d3b68d1dd6b0a9a55da28eea8 Mon Sep 17 00:00:00 2001 From: Henry Winkel Date: Sat, 16 May 2026 14:48:58 +0200 Subject: [PATCH] UI: add JobHistory component and render on stock detail page\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- app/components/JobHistory.tsx | 94 +++++++++++++++++++++++++++++++++++ app/routes/analyze.ticker.tsx | 4 ++ 2 files changed, 98 insertions(+) create mode 100644 app/components/JobHistory.tsx diff --git a/app/components/JobHistory.tsx b/app/components/JobHistory.tsx new file mode 100644 index 0000000..c27ae2a --- /dev/null +++ b/app/components/JobHistory.tsx @@ -0,0 +1,94 @@ +import React, { useEffect, useState } from "react"; + +interface Props { + ticker: string; +} + +export default function JobHistory({ ticker }: Props) { + const [jobs, setJobs] = useState([]); + const [loading, setLoading] = useState(false); + const [selected, setSelected] = useState(null); + + const fetchJobs = async () => { + setLoading(true); + try { + const res = await fetch(`/api/jobs?ticker=${encodeURIComponent(ticker)}`); + if (!res.ok) { + setJobs([]); + } else { + const data = await res.json(); + setJobs(data.jobs || []); + } + } catch (e) { + console.warn("Failed to fetch jobs:", e); + setJobs([]); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchJobs(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ticker]); + + const fetchDetails = async (jobId: string) => { + try { + const res = await fetch(`/api/jobs/${jobId}`); + if (!res.ok) { + setSelected({ id: jobId, error: true }); + return; + } + const data = await res.json(); + setSelected(data); + } catch (e) { + console.warn("Failed to fetch job details:", e); + setSelected({ id: jobId, error: true }); + } + }; + + return ( +
+

Job History

+
+ + {loading ? "Loading..." : `${jobs.length} jobs`} +
+
+ {jobs.length === 0 ? ( +

No recent jobs for {ticker}

+ ) : ( + jobs.map((j: any) => ( +
+
+
+
Job: {j.id}
+
State: {j.state}
+
+
+ API + +
+
+ + {selected?.id === j.id && ( +
{JSON.stringify(selected, null, 2)}
+ )} +
+ )) + )} +
+
+ ); +} diff --git a/app/routes/analyze.ticker.tsx b/app/routes/analyze.ticker.tsx index b86224a..0f9dec9 100644 --- a/app/routes/analyze.ticker.tsx +++ b/app/routes/analyze.ticker.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from "react"; import { useLoaderData, useNavigate, useLocation } from "react-router"; import TradingViewChart from "../components/TradingViewChart"; import Navbar from "../components/Navbar"; +import JobHistory from "../components/JobHistory"; import type { TradingDecision, AnalystReport, DebateRound } from "../types/agents"; export const meta = () => [{ title: "Stock Detail - AITrader" }]; @@ -295,6 +296,9 @@ export default function StockDetail() { )} )} + + {/* Job history */} +