diff --git a/frontend/src/api/aiSessions.ts b/frontend/src/api/aiSessions.ts index b5590f03..728d4565 100644 --- a/frontend/src/api/aiSessions.ts +++ b/frontend/src/api/aiSessions.ts @@ -11,6 +11,7 @@ import type { AISessionSummary, AISessionDetail, AISessionSearchResult, + SimilarSession, PickupSessionRequest, } from '@/types/ai-session' @@ -122,6 +123,13 @@ export const aiSessionsApi = { }) return response.data }, + + async getSimilar(sessionId: string, limit: number = 5): Promise { + const response = await apiClient.get(`/ai-sessions/${sessionId}/similar`, { + params: { limit }, + }) + return response.data + }, } export default aiSessionsApi diff --git a/frontend/src/components/flowpilot/FlowPilotSession.tsx b/frontend/src/components/flowpilot/FlowPilotSession.tsx index 715272f7..b97fc753 100644 --- a/frontend/src/components/flowpilot/FlowPilotSession.tsx +++ b/frontend/src/components/flowpilot/FlowPilotSession.tsx @@ -13,6 +13,7 @@ import { FlowPilotStepCard } from './FlowPilotStepCard' import { FlowPilotActionBar } from './FlowPilotActionBar' import { SessionDocView } from './SessionDocView' import { SessionTicketCard } from './SessionTicketCard' +import { SimilarSessions } from './SimilarSessions' import { TicketPickerModal } from '@/components/session/TicketPickerModal' import { aiSessionsApi } from '@/api' import { toast } from '@/lib/toast' @@ -185,6 +186,7 @@ export function FlowPilotSession({ )} + )} @@ -291,6 +293,9 @@ export function FlowPilotSession({ + + {/* Similar sessions */} + diff --git a/frontend/src/components/flowpilot/SimilarSessions.tsx b/frontend/src/components/flowpilot/SimilarSessions.tsx new file mode 100644 index 00000000..f44efa75 --- /dev/null +++ b/frontend/src/components/flowpilot/SimilarSessions.tsx @@ -0,0 +1,95 @@ +import { useEffect, useState } from 'react' +import { Link } from 'react-router-dom' +import { Loader2 } from 'lucide-react' +import { aiSessionsApi } from '@/api' +import type { SimilarSession } from '@/types/ai-session' +import { cn } from '@/lib/utils' + +interface SimilarSessionsProps { + sessionId: string +} + +export function SimilarSessions({ sessionId }: SimilarSessionsProps) { + const [sessions, setSessions] = useState([]) + const [loading, setLoading] = useState(true) + + useEffect(() => { + let cancelled = false + setLoading(true) + aiSessionsApi + .getSimilar(sessionId, 5) + .then((data) => { + if (!cancelled) setSessions(data) + }) + .catch(() => { + // Silently ignore errors — don't clutter the UI + }) + .finally(() => { + if (!cancelled) setLoading(false) + }) + return () => { + cancelled = true + } + }, [sessionId]) + + if (loading) { + return ( +
+ + Loading similar sessions… +
+ ) + } + + if (sessions.length === 0) { + return null + } + + return ( +
+

+ Similar Past Sessions +

+ {sessions.map((session) => ( + +
+

+ {session.problem_summary || 'Untitled session'} +

+ + {Math.round(session.similarity * 100)}% + +
+ {session.resolution_summary && ( +

+ ✓ {session.resolution_summary} +

+ )} +
+ {session.problem_domain && ( + + {session.problem_domain} + + )} + + {session.status} + +
+ + ))} +
+ ) +} diff --git a/frontend/src/types/ai-session.ts b/frontend/src/types/ai-session.ts index 6a7ed3c1..b1465f5b 100644 --- a/frontend/src/types/ai-session.ts +++ b/frontend/src/types/ai-session.ts @@ -149,3 +149,13 @@ export interface AISessionSearchResult { status: string created_at: string } + +export interface SimilarSession { + id: string + problem_summary: string | null + problem_domain: string | null + status: string + resolution_summary: string | null + created_at: string | null + similarity: number +}