~bigbes/lethe

ref: d2a77459be9c2a2a61326e3bce1e64b749bda522 lethe/web/src/routes/search.tsx -rw-r--r-- 3.0 KiB
d2a77459 — Eugene Blikh fix: store full opencode tool output and display it in session view 23 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import React from 'react'
import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { SubBar } from '../shell/SubBar'
import { Sub, Tag } from '../primitives'
import { AuthGate } from '../shell/AuthGate'
import { AuthError, APIError } from '../api/client'
import { useSearch } from '../features/search/useSearch'
import type { SearchFilters } from '../features/search/useSearch'
import { SearchFilters as SearchFiltersCmp } from '../features/search/SearchFilters'
import { SearchTable } from '../features/search/SearchTable'
import { SaveSearchForm } from '../features/search/SaveSearchForm'
import '../styles/search.css'

type SearchParams = {
  q?: string
  tool?: string
  host?: string
}

export const Route = createFileRoute('/search')({
  validateSearch: (search: Record<string, unknown>): SearchParams => {
    const q = typeof search['q'] === 'string' && search['q'] !== '' ? search['q'] : undefined
    const tool = typeof search['tool'] === 'string' && search['tool'] !== '' ? search['tool'] : undefined
    const host = typeof search['host'] === 'string' && search['host'] !== '' ? search['host'] : undefined
    return { q, tool, host }
  },
  component: SearchRoute,
})

function SearchRoute(): React.JSX.Element {
  const navigate = useNavigate()
  const search = Route.useSearch()

  const filters: SearchFilters = {
    q: search.q ?? '',
    tool: search.tool,
    host: search.host,
  }

  const { results, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, error } = useSearch(
    filters,
    search.q != null,
  )

  function handleFilterChange(next: SearchFilters) {
    void navigate({
      to: '/search',
      search: {
        q: next.q || undefined,
        tool: next.tool || undefined,
        host: next.host || undefined,
      },
    })
  }

  if (isLoading) {
    return (
      <>
        <SubBar>
          <SearchFiltersCmp value={filters} onChange={handleFilterChange} />
        </SubBar>
        <div className="body body-pad">
          <Sub>searching</Sub>
        </div>
      </>
    )
  }

  if (error != null && !(error instanceof AuthError)) {
    const detail = error instanceof APIError ? error.message : String(error)
    return (
      <div className="body body-pad" style={{ display: 'flex', justifyContent: 'center', paddingTop: 60 }}>
        <div className="card" style={{ padding: '24px 32px', textAlign: 'center' }}>
          <div className="uppercase-mono" style={{ marginBottom: 8 }}>error</div>
          <div className="muted">{detail}</div>
        </div>
      </div>
    )
  }

  return (
    <AuthGate>
      <>
        <SubBar right={search.q != null ? <SaveSearchForm query={search.q} /> : undefined}>
          {search.q != null && <Tag kind="neutral">&ldquo;{search.q}&rdquo;</Tag>}
          <SearchFiltersCmp value={filters} onChange={handleFilterChange} />
        </SubBar>
        <SearchTable
          rows={results}
          hasMore={hasNextPage}
          loadingMore={isFetchingNextPage}
          onLoadMore={fetchNextPage}
        />
      </>
    </AuthGate>
  )
}