Add book title to autocomplete suggestions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -756,13 +756,14 @@ async def list_jobs(
|
||||
|
||||
@app.get("/api/jobs/suggestions")
|
||||
async def job_suggestions():
|
||||
"""Return distinct values for author, chapter, and reviewer_name to power autocomplete."""
|
||||
"""Return distinct values for author, book, chapter, and reviewer_name to power autocomplete."""
|
||||
try:
|
||||
with get_db() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
SELECT
|
||||
array_remove(array_agg(DISTINCT author ORDER BY author), NULL) AS authors,
|
||||
array_remove(array_agg(DISTINCT book ORDER BY book), NULL) AS books,
|
||||
array_remove(array_agg(DISTINCT chapter ORDER BY chapter), NULL) AS chapters,
|
||||
array_remove(array_agg(DISTINCT reviewer_name ORDER BY reviewer_name), NULL) AS reviewers
|
||||
FROM ocr_jobs
|
||||
@@ -774,6 +775,7 @@ async def job_suggestions():
|
||||
|
||||
return JSONResponse({
|
||||
"authors": row["authors"] or [],
|
||||
"books": row["books"] or [],
|
||||
"chapters": row["chapters"] or [],
|
||||
"reviewers": row["reviewers"] or [],
|
||||
})
|
||||
|
||||
@@ -274,13 +274,16 @@ function App() {
|
||||
<datalist id="rv-authors">
|
||||
{suggestions.authors.map(a => <option key={a} value={a} />)}
|
||||
</datalist>
|
||||
<datalist id="rv-books">
|
||||
{(suggestions.books || []).map(b => <option key={b} value={b} />)}
|
||||
</datalist>
|
||||
<datalist id="rv-chapters">
|
||||
{suggestions.chapters.map(c => <option key={c} value={c} />)}
|
||||
</datalist>
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{[
|
||||
{ key: 'author', label: 'Author', placeholder: 'Author name', list: 'rv-authors' },
|
||||
{ key: 'book', label: 'Book', placeholder: 'Book title', list: undefined },
|
||||
{ key: 'book', label: 'Book', placeholder: 'Book title', list: 'rv-books' },
|
||||
{ key: 'chapter', label: 'Chapter', placeholder: 'Chapter', list: 'rv-chapters' },
|
||||
{ key: 'page', label: 'Page', placeholder: 'Page number', list: undefined },
|
||||
].map(({ key, label, placeholder, list }) => (
|
||||
|
||||
@@ -181,6 +181,9 @@ function JobDetail({ jobId, onClose, onReviewed, suggestions = {} }) {
|
||||
<datalist id="jd-authors">
|
||||
{(suggestions.authors || []).map(a => <option key={a} value={a} />)}
|
||||
</datalist>
|
||||
<datalist id="jd-books">
|
||||
{(suggestions.books || []).map(b => <option key={b} value={b} />)}
|
||||
</datalist>
|
||||
<datalist id="jd-chapters">
|
||||
{(suggestions.chapters || []).map(c => <option key={c} value={c} />)}
|
||||
</datalist>
|
||||
@@ -194,7 +197,7 @@ function JobDetail({ jobId, onClose, onReviewed, suggestions = {} }) {
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-gray-400 mb-1 block">Book</label>
|
||||
<input type="text" value={editBook} onChange={e => setEditBook(e.target.value)} placeholder="Book title" className={INPUT_CLASS} />
|
||||
<input type="text" list="jd-books" value={editBook} onChange={e => setEditBook(e.target.value)} placeholder="Book title" className={INPUT_CLASS} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-gray-400 mb-1 block">Chapter</label>
|
||||
@@ -355,6 +358,9 @@ export default function JobsPanel() {
|
||||
<datalist id="jp-authors">
|
||||
{suggestions.authors.map(a => <option key={a} value={a} />)}
|
||||
</datalist>
|
||||
<datalist id="jp-books">
|
||||
{(suggestions.books || []).map(b => <option key={b} value={b} />)}
|
||||
</datalist>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<select value={filterStatus} onChange={e => setFilterStatus(e.target.value)} className={INPUT_CLASS}>
|
||||
<option value="">All statuses</option>
|
||||
@@ -362,7 +368,7 @@ export default function JobsPanel() {
|
||||
<option value="reviewed">Reviewed</option>
|
||||
</select>
|
||||
<input type="text" list="jp-authors" value={filterAuthor} onChange={e => setFilterAuthor(e.target.value)} placeholder="Author..." className={INPUT_CLASS} />
|
||||
<input type="text" value={filterBook} onChange={e => setFilterBook(e.target.value)} placeholder="Book..." className={INPUT_CLASS} />
|
||||
<input type="text" list="jp-books" value={filterBook} onChange={e => setFilterBook(e.target.value)} placeholder="Book..." className={INPUT_CLASS} />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
@@ -2,7 +2,7 @@ import { BookOpen } from 'lucide-react'
|
||||
|
||||
export default function MetadataForm({ metadata, onChange, suggestions = {} }) {
|
||||
const { author, book, chapter, page } = metadata
|
||||
const { authors = [], chapters = [] } = suggestions
|
||||
const { authors = [], books = [], chapters = [] } = suggestions
|
||||
|
||||
const field = (key) => (e) => onChange({ ...metadata, [key]: e.target.value })
|
||||
|
||||
@@ -20,6 +20,9 @@ export default function MetadataForm({ metadata, onChange, suggestions = {} }) {
|
||||
<datalist id="mf-authors">
|
||||
{authors.map(a => <option key={a} value={a} />)}
|
||||
</datalist>
|
||||
<datalist id="mf-books">
|
||||
{books.map(b => <option key={b} value={b} />)}
|
||||
</datalist>
|
||||
<datalist id="mf-chapters">
|
||||
{chapters.map(c => <option key={c} value={c} />)}
|
||||
</datalist>
|
||||
@@ -40,6 +43,7 @@ export default function MetadataForm({ metadata, onChange, suggestions = {} }) {
|
||||
<label className="text-xs text-gray-400 mb-1 block">Book</label>
|
||||
<input
|
||||
type="text"
|
||||
list="mf-books"
|
||||
value={book}
|
||||
onChange={field('book')}
|
||||
placeholder="Book title"
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'
|
||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000/api'
|
||||
|
||||
export function useSuggestions() {
|
||||
const [suggestions, setSuggestions] = useState({ authors: [], chapters: [], reviewers: [] })
|
||||
const [suggestions, setSuggestions] = useState({ authors: [], books: [], chapters: [], reviewers: [] })
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`${API_BASE}/jobs/suggestions`)
|
||||
|
||||
Reference in New Issue
Block a user