fix: restore green builds and align frontend-backend contracts (P0)
- Isolate Settings tests from .env and process env leakage - Fix analytics metadata test to unwrap psycopg Json wrapper - Remove unused state variables causing frontend build failures - Fix ReviewPage to use /classifications endpoint instead of nonexistent /result - Normalize ReviewPage status enums (failed not error) and access_type values - Align api.ts types with backend response shapes (ReplayPage, AnalyticsData, AgentUsage)
This commit is contained in:
@@ -39,17 +39,16 @@ export interface ReplayStep {
|
||||
|
||||
export interface ReplayPage {
|
||||
thread_id: string;
|
||||
steps: ReplayStep[];
|
||||
total: number;
|
||||
total_steps: number;
|
||||
page: number;
|
||||
per_page: number;
|
||||
steps: ReplayStep[];
|
||||
}
|
||||
|
||||
export interface AgentUsage {
|
||||
agent_name: string;
|
||||
message_count: number;
|
||||
total_tokens: number;
|
||||
total_cost_usd: number;
|
||||
agent: string;
|
||||
count: number;
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
export interface InterruptStats {
|
||||
@@ -60,14 +59,12 @@ export interface InterruptStats {
|
||||
}
|
||||
|
||||
export interface AnalyticsData {
|
||||
range: string;
|
||||
total_conversations: number;
|
||||
resolved_conversations: number;
|
||||
escalated_conversations: number;
|
||||
resolution_rate: number;
|
||||
escalation_rate: number;
|
||||
total_tokens: number;
|
||||
total_cost_usd: number;
|
||||
avg_turns_per_conversation: number;
|
||||
avg_cost_per_conversation_usd: number;
|
||||
agent_usage: AgentUsage[];
|
||||
interrupt_stats: InterruptStats;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useState } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { ReplayTimeline } from "../components/ReplayTimeline";
|
||||
|
||||
@@ -16,8 +15,6 @@ const MOCK_STEPS = [
|
||||
export function ReplayPage() {
|
||||
const { threadId } = useParams<{ threadId: string }>();
|
||||
const navigate = useNavigate();
|
||||
const [page, setPage] = useState(1);
|
||||
|
||||
if (!threadId) return null;
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useEffect, useRef, useState } from "react";
|
||||
|
||||
interface ImportJob {
|
||||
job_id: string;
|
||||
status: "pending" | "processing" | "done" | "error";
|
||||
error?: string;
|
||||
status: "pending" | "processing" | "done" | "failed";
|
||||
error_message?: string;
|
||||
}
|
||||
|
||||
interface EndpointClassification {
|
||||
@@ -14,16 +14,9 @@ interface EndpointClassification {
|
||||
agent_group: string;
|
||||
}
|
||||
|
||||
interface JobResult {
|
||||
job_id: string;
|
||||
status: string;
|
||||
endpoints: EndpointClassification[];
|
||||
}
|
||||
|
||||
export function ReviewPage() {
|
||||
const [url, setUrl] = useState("");
|
||||
const [job, setJob] = useState<ImportJob | null>(null);
|
||||
const [result, setResult] = useState<JobResult | null>(null);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [submitError, setSubmitError] = useState<string | null>(null);
|
||||
const [classifications, setClassifications] = useState<EndpointClassification[]>([
|
||||
@@ -45,7 +38,7 @@ export function ReviewPage() {
|
||||
path: "/api/v1/payments/{charge_id}/refund",
|
||||
method: "post",
|
||||
summary: "Issue a full or partial refund for a charge",
|
||||
access_type: "admin",
|
||||
access_type: "write",
|
||||
agent_group: "Billing Assistant",
|
||||
},
|
||||
{
|
||||
@@ -78,14 +71,20 @@ export function ReviewPage() {
|
||||
const j: ImportJob = data.data ?? data;
|
||||
setJob(j);
|
||||
if (j.status === "done") {
|
||||
return fetch(`/api/openapi/jobs/${encodeURIComponent(jobId)}/result`)
|
||||
return fetch(`/api/openapi/jobs/${encodeURIComponent(jobId)}/classifications`)
|
||||
.then((r) => r.json())
|
||||
.then((rdata) => {
|
||||
const res: JobResult = rdata.data ?? rdata;
|
||||
setResult(res);
|
||||
setClassifications(res.endpoints ?? []);
|
||||
.then((clfs: EndpointClassification[]) => {
|
||||
setClassifications(
|
||||
clfs.map((c: any) => ({
|
||||
path: c.endpoint?.path ?? c.path ?? "",
|
||||
method: c.endpoint?.method ?? c.method ?? "",
|
||||
summary: c.endpoint?.summary ?? c.summary ?? "",
|
||||
access_type: c.access_type ?? "read",
|
||||
agent_group: c.agent_group ?? "Unassigned",
|
||||
}))
|
||||
);
|
||||
});
|
||||
} else if (j.status === "error") {
|
||||
} else if (j.status === "failed") {
|
||||
return;
|
||||
} else {
|
||||
pollRef.current = setTimeout(() => pollJob(jobId), 2000);
|
||||
@@ -102,7 +101,6 @@ export function ReviewPage() {
|
||||
setSubmitting(true);
|
||||
setSubmitError(null);
|
||||
setJob(null);
|
||||
setResult(null);
|
||||
setClassifications([]);
|
||||
|
||||
fetch("/api/openapi/import", {
|
||||
@@ -174,10 +172,10 @@ export function ReviewPage() {
|
||||
{job && (
|
||||
<div style={{ padding: "1rem", background: "var(--bg-surface)", border: "1px solid var(--border-light)", borderRadius: "var(--radius-md)", marginBottom: "1.5rem" }}>
|
||||
<strong>Job:</strong> {job.job_id} — Status:{" "}
|
||||
<span style={{ fontWeight: 600, color: job.status === "done" ? "#10b981" : job.status === "error" ? "var(--brand-accent)" : "#f59e0b" }}>
|
||||
<span style={{ fontWeight: 600, color: job.status === "done" ? "#10b981" : job.status === "failed" ? "var(--brand-accent)" : "#f59e0b" }}>
|
||||
{job.status}
|
||||
</span>
|
||||
{job.error && <div style={{ marginTop: "4px", color: "var(--brand-accent)" }}>{job.error}</div>}
|
||||
{job.error_message && <div style={{ marginTop: "4px", color: "var(--brand-accent)" }}>{job.error_message}</div>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -221,7 +219,6 @@ export function ReviewPage() {
|
||||
>
|
||||
<option value="read">Read Only</option>
|
||||
<option value="write">Write (Confirm)</option>
|
||||
<option value="admin">Admin</option>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"root":["./src/app.tsx","./src/api.ts","./src/main.tsx","./src/types.ts","./src/components/agentaction.tsx","./src/components/chatinput.tsx","./src/components/chatmessages.tsx","./src/components/errorbanner.tsx","./src/components/interruptprompt.tsx","./src/components/layout.tsx","./src/components/metriccard.tsx","./src/components/navbar.tsx","./src/components/replaytimeline.tsx","./src/hooks/usewebsocket.ts","./src/pages/chatpage.tsx","./src/pages/dashboardpage.tsx","./src/pages/replaylistpage.tsx","./src/pages/replaypage.tsx","./src/pages/reviewpage.tsx"],"version":"5.7.3"}
|
||||
{"root":["./src/app.tsx","./src/api.ts","./src/main.tsx","./src/types.ts","./src/vite-env.d.ts","./src/components/agentaction.tsx","./src/components/chatinput.tsx","./src/components/chatmessages.tsx","./src/components/errorbanner.tsx","./src/components/interruptprompt.tsx","./src/components/layout.tsx","./src/components/metriccard.tsx","./src/components/navbar.tsx","./src/components/replaytimeline.tsx","./src/hooks/usewebsocket.ts","./src/pages/chatpage.tsx","./src/pages/dashboardpage.tsx","./src/pages/replaylistpage.tsx","./src/pages/replaypage.tsx","./src/pages/reviewpage.tsx"],"version":"5.7.3"}
|
||||
Reference in New Issue
Block a user