Add catalog search on timeline header and fix Back to Timeline navigation.

Public GET /api/search over artists, movements, and paintings with a debounced header bar on the timeline; Back to Timeline resets zoom and gallery session.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-07-15 10:34:29 +03:00
co-authored by Cursor
parent 50fc253ab2
commit acc4a91a08
17 changed files with 827 additions and 12 deletions
+71
View File
@@ -171,6 +171,76 @@ Movements are filtered to those with at least one artist active in the requested
---
## `GET /api/search`
Public catalog search over **artists**, **paintings**, and **art movements**. Used by the timeline header search bar (`CatalogSearchBar.tsx`).
**Query**
| Param | Type | Default | Description |
|-------|------|---------|-------------|
| `q` | string | — | Search text (min **2** characters after trim; shorter returns `{ q, results: [] }`) |
| `limit` | int | 20 | Max results total (capped at **50**) |
| `types` | string | all | Optional comma list: `artist`, `painting`, `movement` |
**Matching (case-insensitive `ILIKE`):**
| Entity | Fields |
|--------|--------|
| Artist | `name`, `wikipedia_title`, movement name |
| Movement | movement `name`, era name |
| Painting | `title`, `wikipedia_title`, `year` (as text), artist name, movement name |
Prefix matches on primary labels (`name` / `title`) rank before substring matches.
**Response**
```json
{
"q": "monet",
"results": [
{
"type": "artist",
"id": 19,
"name": "Claude Monet",
"birth_year": 1840,
"death_year": 1926,
"movement_name": "Impressionism",
"portrait_path": "portraits/Claude_Monet.jpg",
"portrait_thumb_path": "portraits/thumbs/Claude_Monet_thumb.jpg"
},
{
"type": "movement",
"id": 12,
"name": "Impressionism",
"color": "#87CEEB",
"start_year": 1860,
"end_year": 1890,
"era_name": "Modern"
},
{
"type": "painting",
"id": 241,
"title": "Water Lilies",
"year": 1919,
"artist_id": 19,
"artist_name": "Claude Monet",
"movement_name": "Impressionism",
"thumbnail_path": "paintings/thumbs/Claude_Monet_Water_Lilies_thumb.jpg",
"image_path": "paintings/Claude_Monet_Water_Lilies.jpg"
}
]
}
```
**Indexes:** applied by `npm run dev:migrate` (`db/migrate-search.sql`) or standalone `npm run dev:migrate:search`.
**Client:** `api.search(q, { limit?, types? })`.
**Navigation from search:** choosing a **painting** opens detail with `returnTo: timeline`; the client shows **← Back to Timeline** and calls `goToTimelineHome()` (clears gallery session, resets timeline zoom). Choosing an **artist** or **movement** uses the normal gallery entry handlers.
---
## `GET /api/artists`
Artists for timeline portraits and the movement flow diagram.
@@ -720,6 +790,7 @@ The React client wraps these endpoints in `client/src/api/client.ts`. All reques
| `logoutCurator()` | `POST /api/auth/logout` |
| `api.getBounds()` | `GET /api/bounds` |
| `api.getTimeline(start, end)` | `GET /api/timeline` |
| `api.search(q, options?)` | `GET /api/search` |
| `api.getArtists(...)` | `GET /api/artists` |
| `api.getTimelineArtists()` | `GET /api/artists?timeline=1` |
| `api.getArtist(id)` | `GET /api/artists/:id` |