import { useEffect, useState } from 'react' import { useNavigate, useSearchParams } from 'react-router-dom' import { sessionsApi } from '@/api/sessions' import { treesApi } from '@/api/trees' import type { Session, TreeListItem } from '@/types' import type { DateRange } from 'react-day-picker' import { SessionFilters } from '@/components/session/SessionFilters' import type { SessionFilterState } from '@/components/session/SessionFilters' import { Spinner } from '@/components/common/Spinner' import { EmptyState } from '@/components/common/EmptyState' import { cn } from '@/lib/utils' import { toast } from '@/lib/toast' import { getSessionResumePath } from '@/lib/routing' export function SessionHistoryPage() { const navigate = useNavigate() const [searchParams, setSearchParams] = useSearchParams() const [sessions, setSessions] = useState([]) const [trees, setTrees] = useState([]) const [isLoading, setIsLoading] = useState(true) const [filter, setFilter] = useState<'all' | 'completed' | 'active'>('all') // Initialize filters from URL params const [filters, setFilters] = useState(() => { const ticketNumber = searchParams.get('ticket') || '' const clientName = searchParams.get('client') || '' const treeName = searchParams.get('tree') || '' const dateType = (searchParams.get('dateType') || 'started') as 'started' | 'completed' const from = searchParams.get('from') const to = searchParams.get('to') const dateRange: DateRange | undefined = from && to ? { from: new Date(from), to: new Date(to) } : undefined return { ticketNumber, clientName, treeName, dateRange, dateType, } }) // Load trees for filter dropdown useEffect(() => { const loadTrees = async () => { try { const treesData = await treesApi.list({}) setTrees(treesData) } catch (err) { console.error('Failed to load trees:', err) } } loadTrees() }, []) // Load sessions when filters change useEffect(() => { loadSessions() }, [filter, filters]) // Update URL params when filters change useEffect(() => { const params = new URLSearchParams() if (filters.ticketNumber) params.set('ticket', filters.ticketNumber) if (filters.clientName) params.set('client', filters.clientName) if (filters.treeName) params.set('tree', filters.treeName) if (filters.dateRange?.from) { params.set('from', filters.dateRange.from.toISOString()) params.set('to', (filters.dateRange.to || filters.dateRange.from).toISOString()) params.set('dateType', filters.dateType) } setSearchParams(params, { replace: true }) }, [filters, setSearchParams]) const loadSessions = async () => { setIsLoading(true) try { const params: Record = {} // Tab filter (all/active/completed) if (filter !== 'all') { params.completed = filter === 'completed' } // Search/filter params if (filters.ticketNumber) { params.ticket_number = filters.ticketNumber } if (filters.clientName) { params.client_name = filters.clientName } if (filters.treeName) { params.tree_name = filters.treeName } // Date range params if (filters.dateRange?.from) { const fromDate = filters.dateRange.from const toDate = filters.dateRange.to || filters.dateRange.from if (filters.dateType === 'started') { params.started_after = fromDate.toISOString() params.started_before = toDate.toISOString() } else { params.completed_after = fromDate.toISOString() params.completed_before = toDate.toISOString() } } const sessionsData = await sessionsApi.list(params) setSessions(sessionsData) } catch (err) { toast.error('Failed to load sessions') console.error(err) } finally { setIsLoading(false) } } const handleFilterChange = (newFilters: SessionFilterState) => { setFilters(newFilters) } const handleClearFilters = () => { setFilters({ ticketNumber: '', clientName: '', treeName: '', dateRange: undefined, dateType: 'started', }) } const formatDate = (dateString: string) => { return new Date(dateString).toLocaleString() } const getTreeName = (session: Session): string => { return session.tree_snapshot?.name || 'Unknown Tree' } const formatOutcomeLabel = (outcome: Session['outcome']): string => { if (!outcome) return 'Not set' return outcome === 'workaround' ? 'Workaround' : outcome.charAt(0).toUpperCase() + outcome.slice(1) } return (

Session History

Search and filter your troubleshooting sessions

{/* Filter Tabs */}
{(['all', 'active', 'completed'] as const).map((tab) => ( ))}
{/* Search and Filter Controls */}
{/* Loading State */} {isLoading ? (
) : sessions.length === 0 ? ( Clear all filters ) : undefined } /> ) : (
{sessions.map((session) => (
{/* Status and Ticket/Client */}
{session.ticket_number || 'No ticket'} {session.client_name && ( {session.client_name} )} {session.completed_at && ( {formatOutcomeLabel(session.outcome)} )}
{/* Tree Name */}

Tree: {getTreeName(session)}

{/* Timestamps */}

Started: {formatDate(session.started_at)} {session.completed_at && ( <> · Completed: {formatDate(session.completed_at)} )}

{/* Stats */}

{session.decisions.length} decision{session.decisions.length !== 1 ? 's' : ''} recorded {session.scratchpad && session.scratchpad.trim() && ( · Has notes )}

{/* Actions */}
{!session.completed_at && ( )}
))}
)}
) } export default SessionHistoryPage