import React, { useState } from 'react'
import { EmptyState, Sub } from '../../primitives'
import { AuthGate } from '../../shell/AuthGate'
import { AuthError, APIError } from '../../api/client'
import type { SavedSearch } from '../../api/adapters'
import {
useSavedSearches,
useCreateSavedSearch,
useUpdateSavedSearch,
useDeleteSavedSearch,
} from './useSavedSearches'
// ── Inline "Add new" form ─────────────────────────────────────────────────────
function AddForm(): React.JSX.Element {
const [name, setName] = useState('')
const [query, setQuery] = useState('')
const createMutation = useCreateSavedSearch()
function handleSave(e: React.FormEvent) {
e.preventDefault()
if (!name.trim() || !query.trim()) return
createMutation.mutate(
{ name: name.trim(), query: query.trim() },
{
onSuccess: () => {
setName('')
setQuery('')
},
},
)
}
return (
<form className="saved-search-form" onSubmit={handleSave}>
<input
className="saved-search-input"
placeholder="name"
value={name}
onChange={(e) => setName(e.target.value)}
autoComplete="off"
/>
<input
className="saved-search-input saved-search-input-mono"
placeholder="query"
value={query}
onChange={(e) => setQuery(e.target.value)}
autoComplete="off"
/>
<button
className="saved-search-btn"
type="submit"
disabled={createMutation.isPending}
>
save
</button>
{createMutation.isError && (
<span className="saved-search-error">
{createMutation.error instanceof APIError
? createMutation.error.message
: String(createMutation.error)}
</span>
)}
</form>
)
}
// ── Per-row inline edit form ──────────────────────────────────────────────────
interface RowProps {
row: SavedSearch
}
function SavedSearchRow({ row }: RowProps): React.JSX.Element {
const [editing, setEditing] = useState(false)
const [name, setName] = useState(row.name)
const [query, setQuery] = useState(row.query)
const updateMutation = useUpdateSavedSearch()
const deleteMutation = useDeleteSavedSearch()
function handleSave(e: React.FormEvent) {
e.preventDefault()
updateMutation.mutate(
{ oldName: row.name, name: name.trim() || undefined, query: query.trim() || undefined },
{ onSuccess: () => setEditing(false) },
)
}
function handleDelete() {
deleteMutation.mutate({ name: row.name })
}
if (editing) {
return (
<div className="saved-searches-row saved-searches-row-editing">
<form className="saved-search-form" onSubmit={handleSave}>
<input
className="saved-search-input"
value={name}
onChange={(e) => setName(e.target.value)}
autoComplete="off"
/>
<input
className="saved-search-input saved-search-input-mono"
value={query}
onChange={(e) => setQuery(e.target.value)}
autoComplete="off"
/>
<button className="saved-search-btn" type="submit" disabled={updateMutation.isPending}>
save
</button>
<button
className="saved-search-btn"
type="button"
onClick={() => {
setEditing(false)
setName(row.name)
setQuery(row.query)
}}
>
cancel
</button>
</form>
{updateMutation.isError && (
<span className="saved-search-error">
{updateMutation.error instanceof APIError
? updateMutation.error.message
: String(updateMutation.error)}
</span>
)}
</div>
)
}
return (
<div className="saved-searches-row">
<span className="saved-searches-cell saved-searches-name">{row.name}</span>
<span className="saved-searches-cell saved-searches-query mono">{row.query}</span>
<span className="saved-searches-cell saved-searches-updated muted">
{new Date(row.updatedAt).toLocaleDateString()}
</span>
<span className="saved-searches-cell saved-searches-actions">
<button
className="saved-search-btn"
type="button"
onClick={() => setEditing(true)}
>
edit
</button>
<button
className="saved-search-btn saved-search-btn-danger"
type="button"
onClick={handleDelete}
disabled={deleteMutation.isPending}
>
delete
</button>
</span>
{deleteMutation.isError && (
<span className="saved-search-error">
{deleteMutation.error instanceof APIError
? deleteMutation.error.message
: String(deleteMutation.error)}
</span>
)}
</div>
)
}
// ── Main section ──────────────────────────────────────────────────────────────
export function SavedSearchesSection(): React.JSX.Element {
const { data, isLoading, error } = useSavedSearches()
if (isLoading) {
return <Sub>loading…</Sub>
}
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>
<div className="saved-searches-section">
<div className="saved-searches-heading">Saved searches</div>
<AddForm />
{data != null && data.length === 0 ? (
<EmptyState glyph="∅" copy="no saved searches yet" />
) : (
<div className="saved-searches-table">
<div className="saved-searches-thead">
<span>Name</span>
<span>Query</span>
<span>Updated</span>
<span></span>
</div>
{data?.map((row) => (
<SavedSearchRow key={row.name} row={row} />
))}
</div>
)}
</div>
</AuthGate>
)
}