refactor(sessions): merge Flow and AI sessions into unified view
Remove the Flow/AI session type toggle tabs. Both session types now display on a single page — FlowPilot sessions at top, flow sessions below, each with their own filters. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,8 +21,6 @@ export function SessionHistoryPage() {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [searchParams, setSearchParams] = useSearchParams()
|
const [searchParams, setSearchParams] = useSearchParams()
|
||||||
|
|
||||||
// Top-level tab: flow sessions vs AI sessions
|
|
||||||
const [sessionType, setSessionType] = useState<'flow' | 'ai'>('flow')
|
|
||||||
const [aiSessions, setAiSessions] = useState<AISessionSummary[]>([])
|
const [aiSessions, setAiSessions] = useState<AISessionSummary[]>([])
|
||||||
const [aiLoading, setAiLoading] = useState(false)
|
const [aiLoading, setAiLoading] = useState(false)
|
||||||
const [aiSearchInput, setAiSearchInput] = useState('')
|
const [aiSearchInput, setAiSearchInput] = useState('')
|
||||||
@@ -166,9 +164,8 @@ export function SessionHistoryPage() {
|
|||||||
setSearchParams(params, { replace: true })
|
setSearchParams(params, { replace: true })
|
||||||
}, [filters, setSearchParams])
|
}, [filters, setSearchParams])
|
||||||
|
|
||||||
// Load AI sessions when tab is active or filters change
|
// Load AI sessions always
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (sessionType !== 'ai') return
|
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
const loadAiSessions = async () => {
|
const loadAiSessions = async () => {
|
||||||
setAiLoading(true)
|
setAiLoading(true)
|
||||||
@@ -190,7 +187,7 @@ export function SessionHistoryPage() {
|
|||||||
}
|
}
|
||||||
loadAiSessions()
|
loadAiSessions()
|
||||||
return () => { cancelled = true }
|
return () => { cancelled = true }
|
||||||
}, [sessionType, aiFilters])
|
}, [aiFilters])
|
||||||
|
|
||||||
const handleFilterChange = (newFilters: SessionFilterState) => {
|
const handleFilterChange = (newFilters: SessionFilterState) => {
|
||||||
setFilters(newFilters)
|
setFilters(newFilters)
|
||||||
@@ -267,66 +264,55 @@ export function SessionHistoryPage() {
|
|||||||
return labels[outcome] ?? outcome
|
return labels[outcome] ?? outcome
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasAiFiltersActive = !!(aiSearchInput || aiFilters.q || aiFilters.problem_domain || aiFilters.confidence_tier || aiFilters.date_from || aiFilters.date_to)
|
||||||
|
const hasFlowFiltersActive = !!(filters.ticketNumber || filters.clientName || filters.treeName || filters.dateRange?.from)
|
||||||
|
|
||||||
|
// Determine section visibility
|
||||||
|
const showAiSection = aiLoading || aiSessions.length > 0 || hasAiFiltersActive
|
||||||
|
const showFlowSection = isLoading || sessions.length > 0 || hasFlowFiltersActive
|
||||||
|
const showCombinedEmpty = !showAiSection && !showFlowSection
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-y-auto h-full">
|
<div className="overflow-y-auto h-full">
|
||||||
<PageMeta title="Session History" />
|
<PageMeta title="Sessions" />
|
||||||
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
|
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h1 className="text-2xl font-heading font-bold text-foreground sm:text-3xl">Session History</h1>
|
<h1 className="text-2xl font-heading font-bold text-foreground sm:text-3xl">Sessions</h1>
|
||||||
<p className="mt-2 text-muted-foreground">
|
<p className="mt-2 text-muted-foreground">
|
||||||
Search and filter your troubleshooting sessions
|
View and manage all your sessions
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Session type toggle */}
|
{showCombinedEmpty && (
|
||||||
<div className="mb-4 flex gap-1 rounded-lg bg-card/50 p-1 w-fit border border-border">
|
<EmptyState
|
||||||
<button
|
illustration={<SessionIllustration />}
|
||||||
onClick={() => setSessionType('flow')}
|
title="No sessions yet"
|
||||||
className={cn(
|
description="Start a flow or FlowPilot session to begin. All your sessions will appear here."
|
||||||
'rounded-md px-4 py-1.5 text-sm font-medium transition-colors',
|
action={
|
||||||
sessionType === 'flow'
|
<div className="flex gap-3">
|
||||||
? 'bg-primary/10 text-foreground'
|
<Link
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
to="/trees"
|
||||||
)}
|
className="inline-flex items-center gap-2 rounded-[10px] bg-gradient-brand px-5 py-2.5 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] transition-all"
|
||||||
>
|
>
|
||||||
Flow Sessions
|
Start a Flow
|
||||||
</button>
|
</Link>
|
||||||
<button
|
<Link
|
||||||
onClick={() => setSessionType('ai')}
|
to="/pilot"
|
||||||
className={cn(
|
className="inline-flex items-center gap-2 rounded-[10px] border border-border bg-[rgba(255,255,255,0.04)] px-5 py-2.5 text-sm font-semibold text-foreground hover:border-[rgba(255,255,255,0.12)] transition-all"
|
||||||
'rounded-md px-4 py-1.5 text-sm font-medium transition-colors',
|
>
|
||||||
sessionType === 'ai'
|
Start AI Session
|
||||||
? 'bg-primary/10 text-foreground'
|
</Link>
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
</div>
|
||||||
)}
|
}
|
||||||
>
|
learnMoreLink="/guides/sessions"
|
||||||
AI Sessions
|
/>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Filter Tabs (flow sessions only) */}
|
|
||||||
{sessionType === 'flow' && (
|
|
||||||
<div className="mb-6 flex gap-2 border-b border-border">
|
|
||||||
{(['active', 'prepared', 'completed', 'all'] as const).map((tab) => (
|
|
||||||
<button
|
|
||||||
key={tab}
|
|
||||||
onClick={() => setFilter(tab)}
|
|
||||||
className={cn(
|
|
||||||
'px-4 py-2 text-sm font-medium transition-colors',
|
|
||||||
filter === tab
|
|
||||||
? 'border-b-2 border-primary text-foreground'
|
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{tab.charAt(0).toUpperCase() + tab.slice(1)}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* AI Sessions view */}
|
{/* FlowPilot Sessions Section */}
|
||||||
{sessionType === 'ai' && (
|
{showAiSection && (
|
||||||
<>
|
<>
|
||||||
|
<h2 className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-3">FlowPilot Sessions</h2>
|
||||||
|
|
||||||
{/* AI Session Filter Bar */}
|
{/* AI Session Filter Bar */}
|
||||||
<div className="glass-card-static p-3 mb-4">
|
<div className="glass-card-static p-3 mb-4">
|
||||||
<div className="flex flex-wrap gap-3 items-center">
|
<div className="flex flex-wrap gap-3 items-center">
|
||||||
@@ -400,7 +386,7 @@ export function SessionHistoryPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Clear filters */}
|
{/* Clear filters */}
|
||||||
{(aiSearchInput || aiFilters.q || aiFilters.problem_domain || aiFilters.confidence_tier || aiFilters.date_from || aiFilters.date_to) && (
|
{hasAiFiltersActive && (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setAiSearchInput('')
|
setAiSearchInput('')
|
||||||
@@ -419,36 +405,21 @@ export function SessionHistoryPage() {
|
|||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
) : aiSessions.length === 0 ? (
|
) : aiSessions.length === 0 ? (
|
||||||
(aiSearchInput || aiFilters.q || aiFilters.problem_domain || aiFilters.confidence_tier || aiFilters.date_from || aiFilters.date_to) ? (
|
<EmptyState
|
||||||
<EmptyState
|
title="No sessions match your filters"
|
||||||
title="No sessions match your filters"
|
description="Try adjusting your search or filters."
|
||||||
description="Try adjusting your search or filters."
|
action={
|
||||||
action={
|
<button
|
||||||
<button
|
onClick={() => {
|
||||||
onClick={() => {
|
setAiSearchInput('')
|
||||||
setAiSearchInput('')
|
setAiFilters({ q: '', problem_domain: '', confidence_tier: '', date_from: '', date_to: '' })
|
||||||
setAiFilters({ q: '', problem_domain: '', confidence_tier: '', date_from: '', date_to: '' })
|
}}
|
||||||
}}
|
className="text-foreground hover:underline text-sm"
|
||||||
className="text-foreground hover:underline text-sm"
|
>
|
||||||
>
|
Clear all filters
|
||||||
Clear all filters
|
</button>
|
||||||
</button>
|
}
|
||||||
}
|
/>
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<EmptyState
|
|
||||||
title="No AI sessions yet"
|
|
||||||
description="Start a FlowPilot session to get AI-guided troubleshooting. Sessions will appear here."
|
|
||||||
action={
|
|
||||||
<Link
|
|
||||||
to="/pilot"
|
|
||||||
className="inline-flex items-center gap-2 rounded-[10px] bg-gradient-brand px-5 py-2.5 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] transition-all"
|
|
||||||
>
|
|
||||||
Start AI Session
|
|
||||||
</Link>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{aiSessions.map((s) => (
|
{aiSessions.map((s) => (
|
||||||
@@ -456,12 +427,37 @@ export function SessionHistoryPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Divider between sections */}
|
||||||
|
{showFlowSection && (
|
||||||
|
<div className="my-8 border-t border-border" />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Flow Sessions Content */}
|
{/* Flow Sessions Section */}
|
||||||
{sessionType === 'flow' && (
|
{showFlowSection && (
|
||||||
<>
|
<>
|
||||||
|
<h2 className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-3">Flow Sessions</h2>
|
||||||
|
|
||||||
|
{/* Filter Tabs */}
|
||||||
|
<div className="mb-6 flex gap-2 border-b border-border">
|
||||||
|
{(['active', 'prepared', 'completed', 'all'] as const).map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab}
|
||||||
|
onClick={() => setFilter(tab)}
|
||||||
|
className={cn(
|
||||||
|
'px-4 py-2 text-sm font-medium transition-colors',
|
||||||
|
filter === tab
|
||||||
|
? 'border-b-2 border-primary text-foreground'
|
||||||
|
: 'text-muted-foreground hover:text-foreground'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{tab.charAt(0).toUpperCase() + tab.slice(1)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<SessionFilters
|
<SessionFilters
|
||||||
filters={filters}
|
filters={filters}
|
||||||
|
|||||||
Reference in New Issue
Block a user