# PocketBase gallery (full page & homepage teaser)

Gallery data can live in PocketBase instead of only `img/` folders or `gallery-manifest.js`. The full gallery page prefers PocketBase when collections exist and are readable.

## Requirements

Use the **same PocketBase URL** as the calendars integration: `WH_POCKETBASE_URL` in `pocketbase-calendars-config.js` (leave empty when the site and API share one origin).

## Public API rules

On **both** collections below, guests need **list** and **view** access so browsers can fetch records and file URLs:

- Set **List rule** and **View rule** to `1 = 1`, or equivalent guest-readable rules (PocketBase does not accept the literal `true` as a rule string).

File URLs use `/api/files/...` and follow the same access rules as the parent record.

## Collections

### `wh_gallery_categories`

| Field   | Type   | Notes                                      |
| ------- | ------ | ------------------------------------------ |
| `slug`  | Text   | Optional URL-safe id; if empty, title is used |
| `title` | Text   | Label in the sidebar (e.g. “Flowers”)     |
| `sort`  | Number | Lower numbers appear first                 |

### `wh_gallery_items`

| Field         | Type     | Relation / notes                                                    |
| ------------- | -------- | ------------------------------------------------------------------- |
| `category`    | Relation | **Single** relation → `wh_gallery_categories` (required for grouping) |
| `media`       | File     | **Single** upload; images (jpg, png, webp, …) or video (mp4, webm, mov). If you prefer, you may name this field **`file`** instead — both names are accepted. |
| `alt`         | Text     | Optional accessibility text (alternate: **`alt_text`**)               |
| `sort`        | Number   | Order within the category                                           |
| `pin_home`    | Bool     | Legacy: only used if there is **no** `home-page` category (see below) |
| `card_size`   | Select   | **`small` \| `medium` \| `large`** — tile size on **index.html** only (added by migration `1739013200_wh_gallery_homepage_card_size.js`) |

Items without a valid category appear under **Uncategorized** in the full gallery.

### Homepage strip category (required for PocketBase-driven home gallery)

Create a category with **`slug` = `home-page`** (title e.g. “Home page gallery”). **Only** `wh_gallery_items` linked to this category appear in the Gallery section on `index.html`. Set **`card_size`** on each item to control small / medium / large tiles.

Other categories (e.g. Cabin) still appear on `gallery.html` only. If the `home-page` category does **not** exist, the homepage falls back to the old behaviour: all items, **`pin_home`** first, then category order.

### `wh_home_page_images` (hero + key sections on `index.html`)

Added by migrations **`1739013400_wh_home_page_images.js`** and **`1739013500_wh_home_page_slot_rename.js`** (renames legacy slot IDs if your DB still had the old dropdown values).

These are **fixed layout slots** — not the scrolling gallery strip further down the page.

| **`slot` value (choose in Admin)** | Where it appears on the homepage |
| ---------------------------------- | -------------------------------- |
| **`hero_left_tall`** | Hero · **large tile on the left** of the 3-image mosaic (next to your headline). |
| **`hero_top_right`** | Hero · **small tile, top-right** in the mosaic (flowers tile). |
| **`hero_bottom_right`** | Hero · **small tile, bottom-right** in the mosaic. |
| **`feature_card_cabin`** | Features row · **card 01** (“Self‑Catering Wood Cabin”). Subtle frosted image behind white text. |
| **`flower_picking_card`** | Features row · **card 02** (“Flower picking”) — full background + dark overlay. |
| **`feature_card_photoshoot`** | Features row · **card 03** (“Photoshoot area”). Same frosted style as card 01. |
| **`why_visit_left`** | **“Why visit”** band — **large photo on the left**, checklist on the right. |
| **`bookings_panel_bg`** | **Bookings & enquiries** — floral/texture image under the green gradient behind the heading (form stays on white). |

The dark **“Open for …”** pill on the hero is **fixed text in HTML**, not this collection.

| Field   | Type    | Notes                                                                 |
| ------- | ------- | --------------------------------------------------------------------- |
| `position` | Select | **Recommended editor field** after **`1778599300`**. Direct dropdown in this same table, e.g. “Hero - large photo left”. |
| `media` | File | **Recommended upload field** after **`1778599100`**. Replace this file in Admin; the website uses it before legacy `image`. |
| `label` | Text    | **Friendly position name** (presentable — shows in the Admin grid). Seeds/migrations set defaults — change it anytime for your team. **The frontend still uses only `slot` for positioning.** |
| `slot`  | Select  | Use the table above — one row per homepage region. Wired to **`data-home-bg-slot`**. |
| `image` | File    | Single photo — table view uses **thumbnails** (~160²) after **`1739013800`** migration; full file still served on the web. Required. |
| `alt`   | Text    | Short description; used as **`aria-label`** when you open a record from PocketBase (including feature cards). |
| `sort`  | Number  | If two rows share the same `slot`, the **lower** `sort` wins; ties use record id. |

Create **one record per slot** you want managed from PocketBase (eight layout regions above). Anything missing keeps the built‑in fallback from `styles.css` (Unsplash / plain cards).

**Optional seed:** migration **`1739013600_wh_seed_home_page_images.js`** creates rows from **`pb_public/gallery/website/*.jpg`** when those files exist. Migration **`1739014100_wh_seed_home_page_image_placeholders.js`** creates **all eight slots** with a tiny **`placeholder.png`** so you only need **`pocketbase migrate up`** and can **replace each image** in Admin (skipped for any slot that already has a record).

**Admin table hard to scan?** Run **`1739013800_wh_home_page_images_label_thumb.js`** (`pocketbase migrate up`): adds **`label`**, thumbnails on **`image`**, and fills default labels so you see plain-language positions next to previews. Then **`1739013900_wh_home_page_images_grid_order.js`**: reorders fields so the grid lists **label** and **image** right after **id** (layout key **slot** stays in the same table).

**Want it to work like `wh_gallery_items`?** Run through **`1778599300_wh_home_page_images_simple_editor.js`**. It leaves editors with one collection only: **`wh_home_page_images`**, using a direct **`position`** dropdown and **`media`** upload. Editors should use **`position` + `media`**; old **`slot` + `image`** fields are hidden fallback fields for older records.

**Admin grid still looks like only random `id` values — no `label` / `slot` / image preview columns?**  
PocketBase stores “which columns are hidden” **in your browser** (not in the database). If you opened this collection before extra fields existed, the UI can persist “hide everything except id” forever until you reset it.

1. **Reset column preferences (fixes “only id” in one shot)**  
   In the browser where you open Admin: **F12** → **Application** (Chrome) or **Storage** (Firefox) → **Local Storage** → your PocketBase origin (e.g. `https://willowheights.co.za`). Delete every key whose name **ends with `@hiddenColumns`** (or clear all keys for that origin if you’re fine resetting table layouts for every collection once). Reload Admin and open **`wh_home_page_images`** again — **`label`**, **`image`** (thumbnail), **`slot`**, etc. should appear.

2. **Toggle visible columns**  
   Alternatively: **Collections → `wh_home_page_images`** → beside **Search**, open **Toggle columns / sliders**, then enable **`label`**, **`image`**, **`slot`** (and **`alt`** / **`sort`** if useful).

3. **Migrations on *this* server**  
   The Admin only shows fields that exist in **this** PocketBase DB. Next to `pocketbase`, run **`pocketbase migrate up`** (or rely on service restart if it runs migrate automatically). Deploy migrations through **`1778599300`** on production (single-table **position** dropdown + **media** upload). Sanity check over HTTP (guest rules must allow listing):  
   `https://YOUR-POCKETBASE-HOST/api/collections/wh_home_page_images/records?perPage=1`  
   — JSON items should include **`position`**, **`media`**, **`slot`**, and **`image`**. If those keys are missing, this server did not apply the migrations (wrong `pb_data`, old deploy, etc.) — fixing that is unrelated to clearing browser **`@hiddenColumns`**.

4. **Repair / thumbs / site API**  
   If the JSON is correct after migrate but thumbnails are missing, ensure **`1739013700`** and **`1739013800`** ran. **Settings → `wh_home_page_images` → API Rules**: guest **list** / **view** = **`1 = 1`** so the **website** can read files (this does not control Admin columns).

**Site still shows old Unsplash / CSS images?** Open the browser devtools **Network** tab and check `/api/collections/wh_home_page_images/records` — it should return 200 and each item should include `slot` and `image`. If you see a console warning starting with `[Willow Heights] Home page layout images`, the request failed (rules, wrong host, or PocketBase down). When `WH_POCKETBASE_URL` is empty, the page must be served from the **same origin** as PocketBase (default when PocketBase hosts `pb_public`).

## Behaviour

### Full gallery (`gallery.html`)

1. **PocketBase** — if `loadGalleryFromPocketBase` returns at least one category row, the sidebar lists those categories and media URLs point at PocketBase file storage.
2. **Directory scan** — if PocketBase is empty or errors, the page tries to read `img/` (works on simple static servers with directory listings).
3. **Manifest** — finally falls back to `gallery-manifest.js` from `node scripts/generate-gallery-manifest.js`.

### Homepage (`index.html`)

The scrolling gallery section includes a **Display size** control (Comfortable / Compact / Spacious) that sets the grid density for visitors; the choice is remembered in `localStorage`.

**Fixed hero mosaic, split image, feature card backdrop:** elements with **`data-home-bg-slot`** fetch from **`wh_home_page_images`** via `loadHomepageLayoutImagesFromPocketBase()`. Uploaded images override the **`--home-bg-layer`** variable; missing slots keep built‑in Unsplash URLs in **`styles.css`**.

**Gallery strip (`home-page` category):** `loadHomepageGalleryFromPocketBase(20)` loads items linked to **`home-page`**, using each record’s **`card_size`**. Without that category, the site falls back to legacy ordering (`pin_home`, etc.) or **`homepageGalleryFallback`** in **`script.js`**.

If PocketBase is unavailable, fixed regions keep CSS fallbacks; the strip uses **`homepageGalleryFallback`** when no PB data loads.

## Cross-origin static hosting

If HTML is on another host than PocketBase, set `WH_POCKETBASE_URL` to the PocketBase origin and configure CORS on the server if needed. Image and video `src` URLs will point at that origin.
