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">“{search.q}”</Tag>}
<SearchFiltersCmp value={filters} onChange={handleFilterChange} />
</SubBar>
<SearchTable
rows={results}
hasMore={hasNextPage}
loadingMore={isFetchingNextPage}
onLoadMore={fetchNextPage}
/>
</>
</AuthGate>
)
}