# v97 RC2 · Image Admin backend foundation QA

## Purpose

This release adds the first real backend-ready Image Admin foundation for AI exercise figure generation.

The v97 image runtime remains the same simple layered model:

```text
theme background
+
transparent exercise figure
```

This release adds an admin workflow around that model without adding API keys to frontend code.

## Product decisions carried forward

- Generate **transparent exercise figure only**.
- Theme background stays separate in the app.
- Drafts are created first.
- A draft must be manually approved before it becomes the active figure.
- Future multiple figure variants / athlete templates are supported.
- Prompts are generated in English and are editable before generation.
- Prompt wording is positive/instructional: it describes the desired image instead of listing a long set of negative avoid-rules.
- Image generation is backend-mediated. Provider keys must stay server-side.
- Pass admin, video admin and image admin should be separate surfaces. This release adds the first dedicated **Bildadmin** surface.

## Added frontend files

- `assets/js/services/image-admin-service.js`
- `assets/js/renderers/image-admin-renderer.js`
- `assets/js/actions/image-admin-actions.js`
- `assets/data/image-admin/athlete-templates.json`
- `assets/data/image-admin/status.json`

## Added backend files

- `api/image-admin/health.php`
- `api/image-admin/status.php`
- `api/image-admin/templates.php`
- `api/image-admin/generate.php`
- `api/image-admin/approve.php`
- `api/image-admin/drafts.php`
- `api/image-admin/lib/common.php`
- `api/image-admin/_private/.htaccess`
- `api/image-admin/_private/config.example.php`

## Backend design

The browser calls PHP endpoints:

```text
Frontend Bildadmin
→ api/image-admin/*.php
→ future server-side image provider adapter
→ generated draft files
→ manual approve
→ active figure path
```

No AI provider key is placed in frontend JavaScript.

Current RC2 generation is mock-provider mode. It copies the default transparent `_default.webp` into a draft path, which proves the full write/approve flow before a real provider is connected.

Draft path:

```text
assets/img/exercises/generated-drafts/<exerciseKey>/<templateId>/<draftId>.webp
```

Approved variant path:

```text
assets/img/exercises/figure-variants/<templateId>/<exerciseKey>.webp
```

Active figure path used by the app:

```text
assets/img/exercises/figures/<exerciseKey>.webp
```

Status metadata:

```text
assets/data/image-admin/status.json
```

## New Bildadmin UI

The new top navigation tab opens `imageAdminScreen`.

Bildadmin includes:

- backend health status
- exercise list from the master exercise library service
- image status: missing / draft / approved
- athlete template selector
- theme preview selector
- layered visual preview
- editable English prompt
- generate draft action
- approve draft action

## Athlete templates

The initial template registry lives in:

```text
assets/data/image-admin/athlete-templates.json
```

Initial templates:

- `next-athlete-v1`
- `hockey-athlete-v1`

Both currently use `_default.webp` as the reference placeholder. Real template reference images can be added later without changing the UI contract.

## QA performed

### JS/PHP/static checks

Passed:

```bash
node tests/rc121_image_admin_foundation_smoke.js
find assets/js -name '*.js' -print0 | xargs -0 -n1 node --check
find api/image-admin -name '*.php' -print0 | xargs -0 -n1 php -l
for t in tests/*.js; do node "$t"; done
for t in tests/*.sh; do bash "$t"; done
```

### PHP endpoint smoke via local PHP server

Passed with PHP built-in server:

```bash
php -S 127.0.0.1:8919
curl http://127.0.0.1:8919/api/image-admin/health.php
curl -X POST http://127.0.0.1:8919/api/image-admin/generate.php
curl -X POST http://127.0.0.1:8919/api/image-admin/approve.php
```

Verified:

- health endpoint returns PHP version and writable checks
- generate endpoint creates a mock draft and status entry
- approve endpoint writes active + variant figure paths
- test artifacts were removed after endpoint smoke

Sandbox note: CLI PHP in this sandbox reported `curl: false`. Real provider integration will need `curl` or an equivalent outbound HTTP method on the web host. The health endpoint exposes this explicitly.

### Real-DOM QA

Passed:

```bash
python3 tests/rc121_image_admin_real_dom_qa.py
```

Method:

- renders actual `preview.html`
- inlines actual `next-system.css`
- inlines actual JS
- mocks image-admin PHP endpoints for browser QA
- opens the new Bildadmin screen
- selects an exercise
- verifies English prompt panel
- verifies athlete template + theme selectors
- verifies layered visual preview
- runs generate + approve contract on desktop QA
- verifies no broken local images

## Screenshots

- `qa_screenshots/rc121_image_admin_mobile_390x844.png`
- `qa_screenshots/rc121_image_admin_ipad_820x1180.png`
- `qa_screenshots/rc121_image_admin_desktop_1440x1024.png`

## Known caveats

- This RC does **not** call a real AI image provider yet.
- Mock generation writes `_default.webp` as the draft image to prove filesystem and UI workflow.
- Real provider adapter still needs to be implemented once the web host confirms outbound HTTP/cURL and the API key strategy.
- The current template references are placeholders.
- Deploy-host test is still required, especially `api/image-admin/health.php` on `nextnarvik.no`.

## Recommended deploy test

After upload, open:

```text
https://nextnarvik.no/api/image-admin/health.php
```

Check:

- `ok: true`
- `curl: true` preferred for real provider integration
- writable status/drafts/figures/variants all true

Then open the app, go to **Bildadmin**, select an exercise and test mock **Generera draft → Godkänn draft**.
