Add guided tours and unify left-to-right hall wall hang.
Visitors walk published tours in a 3D hall with stop notes; curators edit drafts via Tour editor. All galleries (artist, movement, tour) place the first work left of the entrance view and the last on the right. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
48bd17e985
commit
5ddc3fd7f0
31 files changed
+1890
-124
No files matched your search
@@ -214,6 +214,32 @@ Public painting detail still exposes read-only `influencedBy` / `influenced` (un
|
||||
|
||||
---
|
||||
|
||||
## Tours
|
||||
|
||||
Base path: `/api/tours`. Full guide: [tours.md](tours.md).
|
||||
|
||||
| Method | Path | Auth | Purpose |
|
||||
|--------|------|------|---------|
|
||||
| `GET` | `/api/tours` | public | Published tour summaries (`id`, `title`, `description`, cover thumb, `stopCount`) |
|
||||
| `GET` | `/api/tours/:id` | public* | Full tour + ordered `paintings` + `stopBodies` (*draft tours require curator session) |
|
||||
| `GET` | `/api/tours/admin` | curator | All tours (any status) |
|
||||
| `POST` | `/api/tours` | curator | Create `{ title, description?, status? }` |
|
||||
| `PATCH` | `/api/tours/:id` | curator | Update title / description / status / cover |
|
||||
| `DELETE` | `/api/tours/:id` | curator | Delete tour + stops |
|
||||
| `PUT` | `/api/tours/:id/stops` | curator | Replace ordered stops `{ stops: [{ paintingId, body }] }` |
|
||||
|
||||
Detail response shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"tour": { "id": 1, "title": "…", "status": "published", "stopCount": 5 },
|
||||
"paintings": [ /* Painting rows in stop order */ ],
|
||||
"stopBodies": { "42": "Tour text for this stop…" }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `GET /api/search`
|
||||
|
||||
Public catalog search over **artists**, **paintings**, and **art movements**. Used by the timeline header search bar (`CatalogSearchBar.tsx`).
|
||||
|
||||
@@ -136,6 +136,33 @@ Short art-history notes shown on painting detail (`PaintingAnnotations.tsx`).
|
||||
|
||||
Applied by `npm run dev:migrate:painting-annotations` (`db/migrate-painting-annotations.sql`). Load data with `npm run dev:update-painting-annotations` (curated entries in `scripts/painting-annotations-data.js`; add `--wikipedia` for intro sentences from each work’s `wikipedia_title`).
|
||||
|
||||
### `tours` / `tour_stops`
|
||||
|
||||
Curated guided tours. See [tours.md](tours.md).
|
||||
|
||||
**`tours`**
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | SERIAL PK | |
|
||||
| `title` | VARCHAR(200) | |
|
||||
| `description` | TEXT | Default `''` |
|
||||
| `status` | VARCHAR(20) | `draft` \| `published` |
|
||||
| `cover_painting_id` | FK → `paintings` | ON DELETE SET NULL |
|
||||
| `created_at` / `updated_at` | TIMESTAMPTZ | `updated_at` via trigger |
|
||||
|
||||
**`tour_stops`**
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | SERIAL PK | |
|
||||
| `tour_id` | FK → `tours` | ON DELETE CASCADE |
|
||||
| `painting_id` | FK → `paintings` | ON DELETE CASCADE; UNIQUE with `tour_id` |
|
||||
| `sort_order` | INTEGER | Visitor / editor order |
|
||||
| `body` | TEXT | English tour notes for the stop (v1) |
|
||||
|
||||
Applied by `npm run dev:migrate` (`db/migrate-tours.sql`).
|
||||
|
||||
### `painting_influences`
|
||||
|
||||
Directed edges: *this painting* was influenced by *that painting*.
|
||||
|
||||
@@ -359,4 +359,5 @@ Remote: https://gitea.mysuperlab.netcraze.pro/Danilka/Art-gallery
|
||||
| [setup.md](setup.md) | Install, env vars, troubleshooting |
|
||||
| [data-and-images.md](data-and-images.md) | Catalog and image pipeline |
|
||||
| [API.md](API.md) | REST endpoints |
|
||||
| [tours.md](tours.md) | Guided tours |
|
||||
| [DB_structure.md](DB_structure.md) | PostgreSQL schema |
|
||||
@@ -6,5 +6,5 @@ this file contains draft for future releases and features
|
||||
4. ~~tool to sync prod /env resources (both ways), db structure, db data, images, users etc~~ — done for catalog DB + images: `npm run harmonize` (schema dev→prod only; users/audit excluded) — [harmonize-dev-prod.md](harmonize-dev-prod.md)
|
||||
5. ~~curator_audit_log should contain log of actions like fixit, checked, upload etc with details for which entity it was made and details what was the action and outcome~~ — done: table + `logCuratorAction` on fix/clear/upload/delete/checkup flags (and translation upsert/publish); see [DB_structure.md](DB_structure.md#curator_audit_log). (UI to browse logs is still item 3.)
|
||||
6. ~~create search by entity (painting, artist, movement)~~ — done: timeline header + `GET /api/search`
|
||||
7. create guided tours (with text/extra infor, set of entities)
|
||||
7. ~~create guided tours (with text/extra infor, set of entities)~~ — done: `tours` / `tour_stops`, public Tours popup + 3D tour hall, curator Tour editor — [tours.md](tours.md)
|
||||
|
||||
+33
-10
@@ -8,7 +8,7 @@ The app is organised as a **drill-down hierarchy**:
|
||||
|
||||
1. **Timeline** — historical eras (Ancient → Contemporary) with definite or fuzzy date boundaries.
|
||||
2. **Movement flow** — art movements as curved SVG streams on the same year axis; documented predecessor→successor branches; portrait thumbnails placed along each stream.
|
||||
3. **3D gallery** — one personal hall per artist *or* a **movement gallery** (click a movement name on the flow diagram): period-themed interiors, chronological wings of up to ~55 works, side-wall hang only.
|
||||
3. **3D gallery** — one personal hall per artist, a **movement gallery** (click a movement name), or a **guided tour** hall (timeline → **Tours**): period-themed or tour wings of up to ~55 works, side-wall hang, visit order left→right.
|
||||
4. **Painting detail** — full work in the centre, *Influenced By* on the left, *Influenced* on the right (paintings, artists, or movements), optional **art-history notes** with image markers, prev/next catalog browsing, optional fullscreen, link to artist biography.
|
||||
5. **Artist biography** — portrait, lifespan, movement, and Wikipedia-sourced intro text (`bio_short` / `bio_full`). With debug mode on, the same image-audit panel as painting detail (portrait search, **Checked** / **Fix it** / **More** / **Clear** / **Upload**).
|
||||
|
||||
@@ -52,6 +52,8 @@ Gallery/
|
||||
│ │ ├── pages/CheckupPage.tsx # Image audit table
|
||||
│ │ ├── pages/TranslationsPage.tsx # Russian translation review
|
||||
│ │ ├── pages/InfluencesPage.tsx # Influence links CRUD + import wizard
|
||||
│ │ ├── pages/ToursPage.tsx # Guided tour editor
|
||||
│ │ ├── components/ToursPopup.tsx # Public published-tours modal
|
||||
│ │ ├── i18n/ # react-i18next bootstrap
|
||||
│ │ └── locales/{en,ru}/ # UI chrome strings
|
||||
│ │ ├── data/historical-events.ts # Timeline event markers (UI)
|
||||
@@ -279,7 +281,9 @@ Only **mousedown** on portraits and movement labels stops propagation (so drag-t
|
||||
|
||||
## Virtual gallery (3D halls)
|
||||
|
||||
The 3D scene supports two modes in `VirtualGallery.tsx`: **artist halls** (personal catalog) and **movement galleries** (full movement collection, chronological).
|
||||
The 3D scene supports three modes in `VirtualGallery.tsx`: **artist halls** (personal catalog), **movement galleries** (full movement collection, chronological), and **guided tours** (curator-ordered stops — [tours.md](tours.md)).
|
||||
|
||||
**Shared wall hang (all modes):** visit order fills the **left wall first**, then the **right**. The **first** work hangs near the entrance on the left (immediately left of the opening view); the **last** hangs near the entrance on the right. Artist halls use chronological order; movement wings use chronological order within each wing; tours use stop order.
|
||||
|
||||
### Artist halls
|
||||
|
||||
@@ -289,9 +293,8 @@ Each artist has **exactly one hall**. The hall is a rectangular room sized to fi
|
||||
|------|----------------|
|
||||
| One hall per artist | `VirtualGallery.tsx` builds a single room from that artist’s paintings |
|
||||
| Catalog depth | Most artists target **≥ 6** notable works via `npm run dev:expand-catalog` and `famous-paintings-data.js`; some masters have larger museum dumps |
|
||||
| Paintings on walls | Works hang on the **back, left, and right** walls in **one row per wall**; room **depth grows** when the catalog is large |
|
||||
| Corridor layout | **15+ paintings:** short back wall (up to 8 works), remaining works on extended **left/right** side walls — a long gallery corridor |
|
||||
| Wall order | On each wall, left → right: **later works on the left**, **earlier works on the right**; undated works sort toward the left |
|
||||
| Paintings on walls | Works hang on the **left and right** walls in **one row per wall**; room **depth grows** when the catalog is large (back wall is for the exit only) |
|
||||
| Wall order | Chronological visit order: first half on the **left** (entrance → back), second half on the **right** (back → entrance); first work left of the opening view, last on the right |
|
||||
| Floor | Herringbone **parquet** procedural texture (`parquetFloorTexture.ts`) |
|
||||
| Wall tint | Gallery walls blend the artist’s **movement colour** into cream plaster tones |
|
||||
| Frame finish | **Unchecked** works: black moulding; **Reviewed** (`checkup_checked`): bright gold moulding at **double width** |
|
||||
@@ -327,7 +330,7 @@ Enter from the home page by clicking a **movement name** on the movement flow (`
|
||||
| One gallery per movement | All paintings by artists in that movement, sorted chronologically |
|
||||
| Wings | Catalog split into wings of up to **55 works** (`movementHallLayout.ts`); large movements (e.g. Baroque) use multiple wings |
|
||||
| Paintings on walls | **Left and right walls only** — back wall reserved for exit, front for passage to the next wing |
|
||||
| Wall order | Along each side wall: **later works on the left**, **earlier on the right** (same convention as artist halls) |
|
||||
| Wall order | Same shared hang as artist halls: first half left (entrance → back), second half right (back → entrance) |
|
||||
| Frame captions | **Year · artist** label below each frame |
|
||||
| Period interior | Each of the 26 seeded movements maps to a unique style in `movement-interior-styles.ts` (Italian palazzo, Baroque palace, NYC loft, white cube, etc.) |
|
||||
| Wall materials | Hi-res **procedural textures** with normal maps (`galleryProceduralTextures.ts`): real-world stone, marble, wood panelling, brick, velvet, stucco — plus **single-colour painted walls** (`painted-lime`, `painted-oil-matte`, `painted-oil-satin`, `painted-emulsion`, `painted-flat`) tinted per movement for Renaissance salons through modern white cubes |
|
||||
@@ -351,6 +354,20 @@ Enter from the home page by clicking a **movement name** on the movement flow (`
|
||||
|
||||
Movement galleries do **not** use the predecessor/successor influence picker — that remains artist-hall only.
|
||||
|
||||
### Guided tour halls
|
||||
|
||||
Enter from the home page **Tours** popup (`GET /api/tours/:id`). Layout reuses the movement winged hall (`mode: 'tour'` in `VirtualGallery.tsx`).
|
||||
|
||||
| Rule | Implementation |
|
||||
|------|----------------|
|
||||
| Visit order | Curator `sort_order` on `tour_stops` (not chronological) |
|
||||
| Wings | Same ~55-per-wing split as movements; order preserved across wings |
|
||||
| Wall hang | Same left-then-right rule as other halls |
|
||||
| Detail | Tour stop text panel; ‹ › walks tour stops |
|
||||
| Exit | Wing navigator / **Exit to Timeline** (no influence picker) |
|
||||
|
||||
Full guide: [tours.md](tours.md).
|
||||
|
||||
### Shared 3D behaviour
|
||||
|
||||
**3D images** use locally cached files only (`galleryImageUrl` in `client/src/api/client.ts`). Remote fetches are too slow for realtime WebGL textures; the client calls `POST /api/artists/:id/preload-images` automatically when entering an **artist** hall (public route — links disk files only). Movement galleries load painting lists from the API without a separate preload step. While a texture is loading, the frame shows the canvas cover instead of a white placeholder. The center of the hall shows **“Loading gallery…”** until the WebGL canvas is ready, then **“Loading paintings…”** until every wall texture has resolved (tracked through `GalleryTextureLoadContext`). The 3D hall stays mounted while painting detail or bio overlays are open; returning remounts the canvas when the hall becomes active again.
|
||||
@@ -359,11 +376,12 @@ Movement galleries do **not** use the predecessor/successor influence picker —
|
||||
|
||||
## Painting detail view
|
||||
|
||||
Opened from the 3D hall (artist or movement wing — click a frame) or from influence thumbnails on another work’s detail page.
|
||||
Opened from the 3D hall (artist, movement, or tour wing — click a frame) or from influence thumbnails on another work’s detail page.
|
||||
|
||||
| Layer | What you see |
|
||||
|-------|----------------|
|
||||
| **Detail** | Centre image, *Influenced By* (left) and *Influenced* (right) — painting thumbnails (full work visible, letterboxed), artist portraits, or movement swatches — position in catalog (e.g. `3 of 12`) |
|
||||
| **Tour notes** | When opened from a guided tour: stop text panel under the image (English body from `tour_stops`) |
|
||||
| **Art history notes** | Numbered markers on the image (when positioned) plus a note list below — short citations from Gombrich, museum catalogs, Wikipedia, etc. (`painting_annotations` table) |
|
||||
| **Fullscreen** | Click the centre image; `Escape` or click anywhere to return to detail only |
|
||||
|
||||
@@ -371,7 +389,7 @@ Opened from the 3D hall (artist or movement wing — click a frame) or from infl
|
||||
|
||||
| Input | Action |
|
||||
|-------|--------|
|
||||
| `‹` / `›` beside image | Previous / next work by the **same artist** (chronological order) |
|
||||
| `‹` / `›` beside image | Previous / next in the **current catalog** — artist chronology, movement chronology, or **tour stop order** |
|
||||
| `←` / `→` | Same as prev / next (disabled while fullscreen is open) |
|
||||
| Click centre image | Open fullscreen lightbox |
|
||||
| Click influence thumbnail | Open that work’s detail (different artist allowed) |
|
||||
@@ -382,7 +400,7 @@ Opened from the 3D hall (artist or movement wing — click a frame) or from infl
|
||||
|
||||
**Navigation rules:**
|
||||
|
||||
- **Catalog browsing** (‹ › / arrow keys) walks the current artist’s works earliest → latest. It does **not** change the back target: after browsing several works, **Back to Gallery** still returns directly to the hall.
|
||||
- **Catalog browsing** (‹ › / arrow keys) walks the active catalog (artist / movement chronology, or tour stops). It does **not** change the back target: after browsing several works, **Back to Gallery** still returns directly to the hall.
|
||||
- **Influence links** push a new detail layer; **Back** from an influenced work returns to the painting you came from (and from there back to the gallery if applicable).
|
||||
- Side-panel influence images use **`object-fit: contain`** so tall or wide works are not cropped (dark letterbox background).
|
||||
- The 3D hall stays mounted in the background while detail is open so nothing is lost on return.
|
||||
@@ -404,6 +422,8 @@ Next to the toggle, **Show more** (checkbox, persisted in `localStorage`) opens
|
||||
- **Movement lineage** (`movement-lineage.ts`) documents art-historical predecessor→successor links for the flow diagram; extend that file to add or correct branches.
|
||||
- **One hall per artist** keeps navigation predictable: enter from the timeline or bio, leave via the single exit or back button.
|
||||
- **Movement galleries** complement artist halls: full movement corpus in period-themed wings, entered from the flow diagram.
|
||||
- **Guided tours** add curator-ordered winged halls with stop text on painting detail — [tours.md](tours.md).
|
||||
- **Wall hang** is shared: first work on the left at the entrance, last on the right.
|
||||
- **Influence-based hall links** connect artists through documented painting relationships, grouped by movement at the exit.
|
||||
- **3D gallery images** use locally cached files only; slow remote fetches would break realtime rendering. The client calls `POST /api/artists/:id/preload-images` automatically when entering an **artist** hall (public route — links disk files only).
|
||||
- **3D gallery session** stays mounted while painting detail or bio overlays are open; returning to the hall remounts the WebGL canvas when it becomes active again.
|
||||
@@ -416,7 +436,7 @@ Next to the toggle, **Show more** (checkbox, persisted in `localStorage`) opens
|
||||
| Role | Who | Can do |
|
||||
|------|-----|--------|
|
||||
| **`user`** | Anonymous visitor (default) | Browse timeline, movement flow, 3D artist/movement halls, painting detail, artist bios, images |
|
||||
| **`curator`** | Named account (`users` table) | Everything above + **Debug mode**, **Checkup**, **Translations**, **Influences**, debug API mutations |
|
||||
| **`curator`** | Named account (`users` table) | Everything above + **Debug mode**, **Checkup**, **Translations**, **Influences**, **Tour editor**, debug API mutations |
|
||||
|
||||
Curators sign in via **Curator login** in the site header. Sessions use an HTTP-only cookie (`gallery.sid`). The UI hides debug controls from guests; the server enforces the same rules on debug/checkup API routes (`401` without a valid session).
|
||||
|
||||
@@ -435,6 +455,8 @@ Curator-only workflow for reviewing and fixing local image files — not part of
|
||||
| **Checkup page** | Home header → **Checkup** (curators only) | Full-catalog table: gallery vs detail thumbnails, search, fix, review flags |
|
||||
| **Translations** | Home header → **Translations** (curators only) | Review/publish Russian `entity_translations` |
|
||||
| **Influences** | Home header → **Influences** (curators only) | List/CRUD influence edges, CSV/JSON/XLSX import wizard, neighborhood graph — [influence-import.md](influence-import.md) |
|
||||
| **Tour editor** | Home header → **Tour editor** (curators only) | Create/publish guided tours and stop text — [tours.md](tours.md) |
|
||||
| **Tours** | Home header → **Tours** (everyone) | Open published tours in a 3D hall — [tours.md](tours.md) |
|
||||
| **Logout** | Home header (curators) | Ends session; hides debug tools |
|
||||
| **Debug panel** | Painting detail or artist bio (bottom-left, when debug mode on) | Search preview + action buttons (six on painting detail, five on artist bio) |
|
||||
|
||||
@@ -481,5 +503,6 @@ See [API.md](API.md#authentication) and [data-and-images.md](data-and-images.md#
|
||||
| [DB_structure.md](DB_structure.md) | Tables and relationships |
|
||||
| [API.md](API.md) | REST endpoints |
|
||||
| [influence-import.md](influence-import.md) | Curator Influences tool — import wizard, CRUD, graph |
|
||||
| [tours.md](tours.md) | Guided tours — editor, public popup, 3D tour hall |
|
||||
| [i18n-russian.md](i18n-russian.md) | Russian UI + entity_translations |
|
||||
| [data-and-images.md](data-and-images.md) | Image pipeline and seeding |
|
||||
@@ -265,7 +265,7 @@ To add or fix a branch, edit `MOVEMENT_LINEAGE` in that file and rebuild the cli
|
||||
|
||||
Separate from movement lineage layout, `client/src/data/movement-interior-styles.ts` defines a **unique 3D interior** for each seeded art movement (26 styles): wall/floor/ceiling textures, trim colours, window style, and architectural details (columns, coffered ceilings, etc.). Textures are generated procedurally in `client/src/utils/galleryProceduralTextures.ts`.
|
||||
|
||||
Wing layout (up to 55 works per wing, side-wall-only hang, window gap placement) lives in `client/src/utils/movementHallLayout.ts`. To change a movement’s look, edit its entry in `movement-interior-styles.ts` and rebuild the client.
|
||||
Wing layout (up to 55 works per wing, side-wall-only hang, window gap placement) lives in `client/src/utils/movementHallLayout.ts`. Visit order fills the **left wall** (first work at the entrance), then the **right** (last work at the entrance). The same hang applies to artist halls and guided tours. To change a movement’s look, edit its entry in `movement-interior-styles.ts` and rebuild the client.
|
||||
|
||||
## Historical event markers (frontend timeline)
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ If migration fails with permission errors, grant schema rights to the app user f
|
||||
|
||||
`npm run dev:migrate` applies `db/migrate-auth.sql` (`users`, `curator_audit_log`, `session` tables). When the `users` table is empty and `CURATOR_USERNAME` / `CURATOR_PASSWORD` are set in `.env`, the first curator account is created automatically.
|
||||
|
||||
After migrate, sign in from the site header (**Curator login**). Debug mode, Checkup, Translations, Influences, and all mutating debug APIs require an active curator session. Anonymous visitors browse the timeline and 3D halls without logging in.
|
||||
After migrate, sign in from the site header (**Curator login**). Debug mode, Checkup, Translations, Influences, Tour editor, and all mutating debug APIs require an active curator session. Anonymous visitors browse the timeline, published Tours, and 3D halls without logging in.
|
||||
|
||||
See [API.md — Authentication](API.md#authentication) and [basics.md — Developer tools](basics.md#developer-tools-image-audit).
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Guided tours
|
||||
|
||||
Curated walkthroughs of selected paintings. Visitors open **Tours** on the timeline, pick a published tour, and walk a 3D hall (`VirtualGallery` mode `tour`). Opening a frame shows stop notes on painting detail; prev/next follow tour order.
|
||||
|
||||
## Status
|
||||
|
||||
| Status | Who sees it |
|
||||
|--------|-------------|
|
||||
| `draft` | Curators only (Tour editor + `GET /api/tours/:id` when signed in) |
|
||||
| `published` | Everyone via Tours popup + public API |
|
||||
|
||||
Tour stop **body** text is English-only in v1 (stored on `tour_stops.body`). UI chrome is EN/RU via i18n.
|
||||
|
||||
## Data model
|
||||
|
||||
Migration: `db/migrate-tours.sql` (applied by `npm run dev:migrate`).
|
||||
|
||||
| Table | Role |
|
||||
|-------|------|
|
||||
| `tours` | Title, description, `status`, optional `cover_painting_id` |
|
||||
| `tour_stops` | Ordered stops: `tour_id`, `painting_id`, `sort_order`, `body` (unique per tour+painting) |
|
||||
|
||||
Included in `scripts/harmonize-db.js` catalog sync.
|
||||
|
||||
## Curator editor
|
||||
|
||||
Home header → **Tour editor** (curators). Create tours, set draft/published, search-add paintings, reorder, edit stop text, save.
|
||||
|
||||
## Visitor flow
|
||||
|
||||
1. Timeline → **Tours** → modal of published tours
|
||||
2. Select tour → `GET /api/tours/:id` → winged 3D hall (left wall first → right wall last, stop order preserved)
|
||||
3. Click frame → painting detail with tour notes + stop-ordered navigation
|
||||
4. Back / hall exit → timeline
|
||||
|
||||
Hall hang rules match artist and movement galleries — see [basics.md — Virtual gallery](basics.md#virtual-gallery-3d-halls).
|
||||
|
||||
## API
|
||||
|
||||
See [API.md](API.md#tours). Audit actions: `tour.create`, `tour.update`, `tour.delete`, `tour.stops`.
|
||||
|
||||
## Out of scope (v1)
|
||||
|
||||
- Russian tour body translations
|
||||
- Auto-generated / AI tours
|
||||
- Audio or timed slideshow
|
||||
- Editing tours from painting detail
|
||||
Reference in new issue
Block a user