diff --git a/packages/workflow-dashboard/package.json b/packages/workflow-dashboard/package.json index 0ec2e1e..c6027d7 100644 --- a/packages/workflow-dashboard/package.json +++ b/packages/workflow-dashboard/package.json @@ -17,6 +17,7 @@ "react": "^19.2.6", "react-dom": "^19.2.6", "react-markdown": "^10.1.0", + "react-router": "^7.15.1", "shiki": "^4.0.2" }, "devDependencies": { diff --git a/packages/workflow-dashboard/src/app.tsx b/packages/workflow-dashboard/src/app.tsx index b96d11e..e0710d1 100644 --- a/packages/workflow-dashboard/src/app.tsx +++ b/packages/workflow-dashboard/src/app.tsx @@ -1,74 +1,35 @@ import { useState } from "react"; +import { Navigate, Outlet, useParams } from "react-router"; import { clearApiKey, hasApiKey } from "./api.ts"; -import { LoginPage } from "./components/login.tsx"; import { RunDialog } from "./components/run-dialog.tsx"; import { Sidebar } from "./components/sidebar.tsx"; import { StatusBar } from "./components/status-bar.tsx"; -import { ThreadDetail } from "./components/thread-detail.tsx"; -import { ThreadList } from "./components/thread-list.tsx"; -import { WorkflowDetail } from "./components/workflow-detail.tsx"; -import { WorkflowList } from "./components/workflow-list.tsx"; -import { useHashRoute } from "./use-hash-route.ts"; -export function App() { +export function Layout() { const [authed, setAuthed] = useState(hasApiKey()); - const { view, client, threadId, workflowName, setView, setClient, setThreadId, setWorkflowName } = - useHashRoute(); + const { client } = useParams(); const [showRun, setShowRun] = useState(false); if (!authed) { - return setAuthed(true)} />; + return ; } return (
{ clearApiKey(); setAuthed(false); }} />
- setShowRun(true)} /> + setShowRun(true)} />
- {!client && ( -
-

- Select an client from the sidebar to get started. -

-
- )} - {client && view === "threads" && threadId === null && ( - - )} - {client && view === "threads" && threadId !== null && ( - setThreadId(null)} /> - )} - {client && view === "workflows" && workflowName === null && ( - - )} - {client && view === "workflows" && workflowName !== null && ( - setWorkflowName(null)} - /> - )} +
{showRun && client && ( - setShowRun(false)} - onCreated={(id) => { - setShowRun(false); - setThreadId(id); - }} - /> + setShowRun(false)} /> )}
); diff --git a/packages/workflow-dashboard/src/components/client-redirect.tsx b/packages/workflow-dashboard/src/components/client-redirect.tsx new file mode 100644 index 0000000..a38ae34 --- /dev/null +++ b/packages/workflow-dashboard/src/components/client-redirect.tsx @@ -0,0 +1,27 @@ +import { Navigate } from "react-router"; +import { listClients } from "../api.ts"; +import { useFetch } from "../hooks.ts"; + +export function ClientRedirect() { + const { status, data } = useFetch(() => listClients(), []); + + if (status === "loading") { + return ( +
+

Loading clients...

+
+ ); + } + + if (status === "ok" && data.length > 0) { + return ; + } + + return ( +
+

+ Select a client from the sidebar to get started. +

+
+ ); +} diff --git a/packages/workflow-dashboard/src/components/login.tsx b/packages/workflow-dashboard/src/components/login.tsx index 6ee1afb..9fff4b2 100644 --- a/packages/workflow-dashboard/src/components/login.tsx +++ b/packages/workflow-dashboard/src/components/login.tsx @@ -1,11 +1,9 @@ import { useState } from "react"; +import { useNavigate } from "react-router"; import { setApiKey } from "../api.ts"; -type Props = { - onLogin: () => void; -}; - -export function LoginPage({ onLogin }: Props) { +export function LoginPage() { + const navigate = useNavigate(); const [key, setKey] = useState(""); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); @@ -40,7 +38,7 @@ export function LoginPage({ onLogin }: Props) { } setApiKey(key.trim()); - onLogin(); + navigate("/", { replace: true }); } return ( diff --git a/packages/workflow-dashboard/src/components/run-dialog.tsx b/packages/workflow-dashboard/src/components/run-dialog.tsx index 843ca13..ba32fc5 100644 --- a/packages/workflow-dashboard/src/components/run-dialog.tsx +++ b/packages/workflow-dashboard/src/components/run-dialog.tsx @@ -1,14 +1,15 @@ import { useState } from "react"; +import { useNavigate } from "react-router"; import { listWorkflows, runThread } from "../api.ts"; import { useFetch } from "../hooks.ts"; type Props = { client: string; onClose: () => void; - onCreated: (threadId: string) => void; }; -export function RunDialog({ client, onClose, onCreated }: Props) { +export function RunDialog({ client, onClose }: Props) { + const navigate = useNavigate(); const workflows = useFetch(() => listWorkflows(client), [client]); const [workflow, setWorkflow] = useState(""); const [prompt, setPrompt] = useState(""); @@ -22,7 +23,8 @@ export function RunDialog({ client, onClose, onCreated }: Props) { setError(null); try { const result = await runThread(client, workflow, prompt); - onCreated(result.threadId); + onClose(); + navigate(`/${client}/threads/${result.threadId}`); } catch (err) { setError(err instanceof Error ? err.message : String(err)); setSubmitting(false); diff --git a/packages/workflow-dashboard/src/components/sidebar.tsx b/packages/workflow-dashboard/src/components/sidebar.tsx index b23aa94..c702597 100644 --- a/packages/workflow-dashboard/src/components/sidebar.tsx +++ b/packages/workflow-dashboard/src/components/sidebar.tsx @@ -1,27 +1,21 @@ -import { useEffect } from "react"; +import { useLocation, useNavigate, useParams } from "react-router"; import type { ClientEndpoint } from "../api.ts"; import { listClients } from "../api.ts"; import { useFetch } from "../hooks.ts"; type Props = { - view: "threads" | "workflows"; - client: string | null; - onViewChange: (v: "threads" | "workflows") => void; - onClientChange: (a: string | null) => void; onLogout: () => void; }; -export function Sidebar({ view, client, onViewChange, onClientChange, onLogout }: Props) { +export function Sidebar({ onLogout }: Props) { + const { client } = useParams(); + const navigate = useNavigate(); + const location = useLocation(); const { status, data } = useFetch(() => listClients(), []); const clients: ClientEndpoint[] = status === "ok" ? data : []; - // Auto-select first client when none is selected - useEffect(() => { - if (client === null && clients.length > 0) { - onClientChange(clients[0].name); - } - }, [client, clients, onClientChange]); + const view = location.pathname.includes("/workflows") ? "workflows" : "threads"; const viewItems = [ { key: "threads" as const, label: "Threads", icon: "⚡" }, @@ -42,7 +36,6 @@ export function Sidebar({ view, client, onViewChange, onClientChange, onLogout }

- {/* Client selector */}
- {/* View navigation */}