import React, { useEffect, useCallback } from 'react' import { createFileRoute, useNavigate } from '@tanstack/react-router' import { SubBar } from '../shell/SubBar' import { Sub } from '../primitives' import { FilterChips } from '../features/home/FilterChips' import { SessionsTable } from '../features/home/SessionsTable' import { useSessions } from '../features/home/useSessions' import { useHomeCursor } from '../features/home/useHomeCursor' import { useKeyboardCursor } from './__root' import { AuthError, APIError } from '../api/client' import type { HomeFilters } from '../features/home/useSessions' import type { Session } from '../api/adapters' import '../styles/home.css' type HomeSearch = { since?: '1d' | '7d' | '30d' | '90d' | 'all' tool?: string host?: string } const VALID_SINCE = new Set(['1d', '7d', '30d', '90d', 'all']) export const Route = createFileRoute('/')({ validateSearch: (search: Record): HomeSearch => { const since = typeof search['since'] === 'string' && VALID_SINCE.has(search['since']) ? (search['since'] as HomeSearch['since']) : undefined const tool = typeof search['tool'] === 'string' && search['tool'] !== '' ? search['tool'] : undefined const host = typeof search['host'] === 'string' && search['host'] !== '' ? search['host'] : undefined return { since, tool, host } }, component: HomeRoute, }) function HomeRoute(): React.JSX.Element { const navigate = useNavigate() const search = Route.useSearch() const filters: HomeFilters = { since: search.since, tool: search.tool, host: search.host, } const { data: sessions, isLoading, error } = useSessions(filters) const handleOpen = useCallback((s: Session) => { void navigate({ to: '/session/$tool/$host/$id', params: { tool: s.tool, host: s.host, id: s.id } }) }, [navigate]) const { cursor, move, activate, jumpTo } = useHomeCursor( sessions?.length ?? 0, useCallback((idx: number) => { if (sessions && sessions[idx]) handleOpen(sessions[idx]) }, [sessions, handleOpen]), ) // Wire our cursor into the global keyboard controller const cursorRef = useKeyboardCursor() useEffect(() => { cursorRef.current = { move, activate } return () => { cursorRef.current = { move: (_d: 1 | -1) => { /* no-op */ }, activate: () => { /* no-op */ } } } }, [cursorRef, move, activate]) function handleFilterChange(next: HomeFilters) { void navigate({ to: '/', search: { since: next.since, tool: next.tool, host: next.host, }, }) } if (isLoading) { return ( <>
loading…
) } if (error != null) { if (error instanceof AuthError) { return (
not authenticated
Sign in to view your sessions.
) } const detail = error instanceof APIError ? error.message : String(error) return (
error
{detail}
) } return ( <> ) }