From 871ad7124a45cc2617a570e0357c0a1634b6b184 Mon Sep 17 00:00:00 2001 From: mikael-lovqvists-claude-agent Date: Wed, 1 Apr 2026 05:04:57 +0000 Subject: [PATCH] Add Images admin section for managing source image uses New top-level nav section showing all source images in a list view with checkboxes to edit the uses array (grid, bin) per image. Allows correcting wrongly-tagged images without code changes. Server PUT /api/source-images/:id was already in place; re-added the frontend API wrapper that was prematurely removed. Co-Authored-By: Claude Sonnet 4.6 --- public/app.mjs | 70 +++++++++++++++++++++++++++++++++++++++++++ public/index.html | 1 + public/lib/api.mjs | 5 ++-- public/style.css | 62 ++++++++++++++++++++++++++++++++++++++ public/templates.html | 21 +++++++++++++ 5 files changed, 157 insertions(+), 2 deletions(-) diff --git a/public/app.mjs b/public/app.mjs index 5829175..165001a 100644 --- a/public/app.mjs +++ b/public/app.mjs @@ -1921,6 +1921,73 @@ function confirm_delete(message, on_confirm) { dlg.showModal(); } +// --------------------------------------------------------------------------- +// Images (source image admin) +// --------------------------------------------------------------------------- + +const KNOWN_USES = ['grid', 'bin']; + +function render_images_section() { + const main = document.getElementById('main'); + main.replaceChildren(document.getElementById('t-section-images').content.cloneNode(true)); + const list_el = document.getElementById('img-admin-list'); + + if (all_sources.length === 0) { + const el = clone('t-empty-block'); + el.textContent = 'No source images yet.'; + list_el.appendChild(el); + return; + } + + for (const src of all_sources) { + const row = clone('t-img-admin-row'); + const link = qs(row, '.img-admin-thumb-link'); + link.href = `/img/${src.id}`; + qs(row, '.img-admin-thumb').src = `/img/${src.id}`; + qs(row, '.img-admin-name').textContent = src.original_name || src.id; + qs(row, '.img-admin-meta').textContent = src.width && src.height ? `${src.width}×${src.height}` : ''; + + const uses_el = qs(row, '.img-admin-uses'); + for (const use of KNOWN_USES) { + const label = document.createElement('label'); + label.className = 'img-admin-use-label'; + const cb = document.createElement('input'); + cb.type = 'checkbox'; + cb.checked = src.uses?.includes(use) ?? false; + cb.addEventListener('change', async () => { + const new_uses = KNOWN_USES.filter(u => { + const el = uses_el.querySelector(`input[data-use="${u}"]`); + return el ? el.checked : src.uses?.includes(u); + }); + try { + const result = await api.update_source_image_uses(src.id, new_uses); + src.uses = result.source.uses; + all_sources = all_sources.map(s => s.id === src.id ? { ...s, uses: src.uses } : s); + } catch (err) { + alert(err.message); + cb.checked = !cb.checked; // revert + } + }); + cb.dataset.use = use; + label.appendChild(cb); + label.append(' ' + use); + uses_el.appendChild(label); + } + + qs(row, '.img-admin-delete').addEventListener('click', async () => { + try { + await api.delete_source_image(src.id); + all_sources = all_sources.filter(s => s.id !== src.id); + row.remove(); + } catch (err) { + alert(err.message); + } + }); + + list_el.appendChild(row); + } +} + // --------------------------------------------------------------------------- // Bins // --------------------------------------------------------------------------- @@ -2087,6 +2154,8 @@ function parse_url() { section = 'fields'; } else if (p0 === 'templates') { section = 'templates'; + } else if (p0 === 'images') { + section = 'images'; } else if (p0 === 'bins') { section = 'bins'; bin_tab = p1 === 'sources' ? 'sources' : 'bins'; @@ -2171,6 +2240,7 @@ function render() { else if (section === 'grids') render_grids(); else if (section === 'templates') render_templates(); else if (section === 'bins') render_bins(); + else if (section === 'images') render_images_section(); } // --------------------------------------------------------------------------- diff --git a/public/index.html b/public/index.html index e7c8bbb..522f603 100644 --- a/public/index.html +++ b/public/index.html @@ -17,6 +17,7 @@ +
diff --git a/public/lib/api.mjs b/public/lib/api.mjs index 9696989..fa38b67 100644 --- a/public/lib/api.mjs +++ b/public/lib/api.mjs @@ -35,8 +35,9 @@ export const update_grid_draft = (id, body) => req('PUT', `/api/grid-drafts/${i export const delete_grid_draft = (id) => req('DELETE', `/api/grid-drafts/${id}`); // Source images -export const get_source_images = () => req('GET', '/api/source-images'); -export const delete_source_image = (id) => req('DELETE', `/api/source-images/${id}`); +export const get_source_images = () => req('GET', '/api/source-images'); +export const update_source_image_uses = (id, uses) => req('PUT', `/api/source-images/${id}`, { uses }); +export const delete_source_image = (id) => req('DELETE', `/api/source-images/${id}`); // Component templates export const get_component_templates = () => req('GET', '/api/component-templates'); diff --git a/public/style.css b/public/style.css index 99d988e..a72029b 100644 --- a/public/style.css +++ b/public/style.css @@ -1844,6 +1844,68 @@ nav { text-decoration: underline; } +/* ===== IMAGES ADMIN ===== */ + +.img-admin-list { + display: flex; + flex-direction: column; + gap: 0; +} + +.img-admin-row { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.5rem 0; + border-bottom: 1px solid var(--border); +} + +.img-admin-thumb-link { + flex-shrink: 0; +} + +.img-admin-thumb { + width: 80px; + height: 56px; + object-fit: cover; + border-radius: 3px; + display: block; +} + +.img-admin-info { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 0.2rem; +} + +.img-admin-name { + font-size: 0.9rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.img-admin-meta { + font-size: 0.75rem; + color: var(--text-faint); +} + +.img-admin-uses { + display: flex; + gap: 0.75rem; +} + +.img-admin-use-label { + font-size: 0.8rem; + color: var(--text-muted); + cursor: pointer; + display: flex; + align-items: center; + gap: 0.25rem; +} + /* ===== BINS ===== */ .bin-gallery { diff --git a/public/templates.html b/public/templates.html index dc6facc..41aac83 100644 --- a/public/templates.html +++ b/public/templates.html @@ -248,6 +248,27 @@
+ + + + +