← back to Wallco Ai
wallco-ai: gate Rate panel on admin, restore See-in-Room generator button
e5bfcadecc4a8382f72c16591821503fed4ecad7 · 2026-05-13 12:44:39 -0700 · SteveStudio2
- Rate panel only renders for admins. The POST /api/design/:id/rate
handler is admin-gated, so non-admins saw the panel but every click
produced 'save error' — render-time gate fixes both surface and root.
- Add See-in-Room generator bar above the rooms gallery (room-type +
angle pickers, Generate Room button). Wired to existing POST /api/room
and dispatches wallco:room-generated so the gallery refreshes in place.
Admin-only since each call hits the upstream renderer (ROOM_API).
Files touched
M data/designs.jsonM server.js
Diff
commit e5bfcadecc4a8382f72c16591821503fed4ecad7
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 13 12:44:39 2026 -0700
wallco-ai: gate Rate panel on admin, restore See-in-Room generator button
- Rate panel only renders for admins. The POST /api/design/:id/rate
handler is admin-gated, so non-admins saw the panel but every click
produced 'save error' — render-time gate fixes both surface and root.
- Add See-in-Room generator bar above the rooms gallery (room-type +
angle pickers, Generate Room button). Wired to existing POST /api/room
and dispatches wallco:room-generated so the gallery refreshes in place.
Admin-only since each call hits the upstream renderer (ROOM_API).
---
data/designs.json | 462 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
server.js | 80 +++++++++-
2 files changed, 538 insertions(+), 4 deletions(-)
diff --git a/data/designs.json b/data/designs.json
index 93c8409..c71887e 100644
--- a/data/designs.json
+++ b/data/designs.json
@@ -74478,5 +74478,467 @@
"user_scale_good": null,
"user_stars": null,
"room_mockups": []
+ },
+ {
+ "id": 3613,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#946645",
+ "saturation": 0.364,
+ "title": "Amber Atelier No.3613",
+ "handle": "wallco-3613",
+ "image_url": "/designs/img/1778701097120_670545827.png",
+ "filename": "1778701097120_670545827.png",
+ "generator": "wallco.ai",
+ "seed": 670545827,
+ "is_published": false,
+ "created_at": "2026-05-13T19:38:34.561649+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3614,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#946645",
+ "saturation": 0.364,
+ "title": "Amber Salon No.3614",
+ "handle": "wallco-3614",
+ "image_url": "/designs/img/var_3613_hue+60_1778701114878.png",
+ "filename": "var_3613_hue+60_1778701114878.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-13T19:38:37.319804+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3615,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#946645",
+ "saturation": 0.364,
+ "title": "Amber Baroque No.3615",
+ "handle": "wallco-3615",
+ "image_url": "/designs/img/var_3613_hue+120_1778701117324.png",
+ "filename": "var_3613_hue+120_1778701117324.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-13T19:38:39.504732+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3616,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#946645",
+ "saturation": 0.364,
+ "title": "Amber Damask No.3616",
+ "handle": "wallco-3616",
+ "image_url": "/designs/img/var_3613_hue+180_1778701119507.png",
+ "filename": "var_3613_hue+180_1778701119507.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-13T19:38:41.682915+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3617,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#946645",
+ "saturation": 0.364,
+ "title": "Amber Heirloom No.3617",
+ "handle": "wallco-3617",
+ "image_url": "/designs/img/var_3613_hue-60_1778701121686.png",
+ "filename": "var_3613_hue-60_1778701121686.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-13T19:38:44.321034+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3618,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#946645",
+ "saturation": 0.364,
+ "title": "Amber Atelier No.3618",
+ "handle": "wallco-3618",
+ "image_url": "/designs/img/var_3613_mono_1778701124345.png",
+ "filename": "var_3613_mono_1778701124345.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-13T19:38:46.924466+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3619,
+ "kind": "seamless_tile",
+ "category": "drunk-animals",
+ "dominant_hex": "#221d21",
+ "saturation": 0.079,
+ "title": "Noir Folly No.3619",
+ "handle": "wallco-3619",
+ "image_url": "/designs/img/1778701171331_388503547.png",
+ "filename": "1778701171331_388503547.png",
+ "generator": "wallco.ai",
+ "seed": 388503547,
+ "is_published": false,
+ "created_at": "2026-05-13T19:39:40.304022+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3620,
+ "kind": "seamless_tile",
+ "category": "stoned-animals",
+ "dominant_hex": "#676954",
+ "saturation": 0.111,
+ "title": "Olive Stealie No.3620",
+ "handle": "wallco-3620",
+ "image_url": "/designs/img/1778701171481_944749873.png",
+ "filename": "1778701171481_944749873.png",
+ "generator": "wallco.ai",
+ "seed": 944749873,
+ "is_published": false,
+ "created_at": "2026-05-13T19:39:43.234407+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3621,
+ "kind": "seamless_tile",
+ "category": "stoned-animals",
+ "dominant_hex": "#372e23",
+ "saturation": 0.222,
+ "title": "Amber Trip No.3621",
+ "handle": "wallco-3621",
+ "image_url": "/designs/img/1778701261994_1216365692.png",
+ "filename": "1778701261994_1216365692.png",
+ "generator": "wallco.ai",
+ "seed": 1216365692,
+ "is_published": false,
+ "created_at": "2026-05-13T19:41:12.892104+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3622,
+ "kind": "seamless_tile",
+ "category": "drunk-animals",
+ "dominant_hex": "#4b252e",
+ "saturation": 0.339,
+ "title": "Rose Tableau No.3622",
+ "handle": "wallco-3622",
+ "image_url": "/designs/img/1778701261993_1028134879.png",
+ "filename": "1778701261993_1028134879.png",
+ "generator": "wallco.ai",
+ "seed": 1028134879,
+ "is_published": false,
+ "created_at": "2026-05-13T19:41:13.424197+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3623,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#3a484a",
+ "saturation": 0.121,
+ "title": "Marine Atelier No.3623",
+ "handle": "wallco-3623",
+ "image_url": "/designs/img/1778701294966_261236032.png",
+ "filename": "1778701294966_261236032.png",
+ "generator": "wallco.ai",
+ "seed": 261236032,
+ "is_published": false,
+ "created_at": "2026-05-13T19:41:43.184939+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3624,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#3a484a",
+ "saturation": 0.121,
+ "title": "Marine Salon No.3624",
+ "handle": "wallco-3624",
+ "image_url": "/designs/img/var_3623_hue+60_1778701303289.png",
+ "filename": "var_3623_hue+60_1778701303289.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-13T19:41:45.375027+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3625,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#3a484a",
+ "saturation": 0.121,
+ "title": "Marine Baroque No.3625",
+ "handle": "wallco-3625",
+ "image_url": "/designs/img/var_3623_hue+120_1778701305380.png",
+ "filename": "var_3623_hue+120_1778701305380.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-13T19:41:47.463367+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3626,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#3a484a",
+ "saturation": 0.121,
+ "title": "Marine Damask No.3626",
+ "handle": "wallco-3626",
+ "image_url": "/designs/img/var_3623_hue+180_1778701307466.png",
+ "filename": "var_3623_hue+180_1778701307466.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-13T19:41:49.515658+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3627,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#3a484a",
+ "saturation": 0.121,
+ "title": "Marine Heirloom No.3627",
+ "handle": "wallco-3627",
+ "image_url": "/designs/img/var_3623_hue-60_1778701309519.png",
+ "filename": "var_3623_hue-60_1778701309519.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-13T19:41:51.576248+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3628,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#3a484a",
+ "saturation": 0.121,
+ "title": "Marine Atelier No.3628",
+ "handle": "wallco-3628",
+ "image_url": "/designs/img/var_3623_mono_1778701311580.png",
+ "filename": "var_3623_mono_1778701311580.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-13T19:41:53.727466+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3629,
+ "kind": "seamless_tile",
+ "category": "drunk-animals",
+ "dominant_hex": "#83575a",
+ "saturation": 0.202,
+ "title": "Rose Folly No.3629",
+ "handle": "wallco-3629",
+ "image_url": "/designs/img/1778701311998_1888951892.png",
+ "filename": "1778701311998_1888951892.png",
+ "generator": "wallco.ai",
+ "seed": 1888951892,
+ "is_published": false,
+ "created_at": "2026-05-13T19:42:00.633368+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3630,
+ "kind": "seamless_tile",
+ "category": "drunk-animals",
+ "dominant_hex": "#be9a61",
+ "saturation": 0.417,
+ "title": "Honey Hiccup No.3630",
+ "handle": "wallco-3630",
+ "image_url": "/designs/img/1778701351335_1951006117.png",
+ "filename": "1778701351335_1951006117.png",
+ "generator": "wallco.ai",
+ "seed": 1951006117,
+ "is_published": false,
+ "created_at": "2026-05-13T19:42:39.58865+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3631,
+ "kind": "seamless_tile",
+ "category": "stoned-animals",
+ "dominant_hex": "#949a7b",
+ "saturation": 0.133,
+ "title": "Olive Trip No.3631",
+ "handle": "wallco-3631",
+ "image_url": "/designs/img/1778701351484_977935512.png",
+ "filename": "1778701351484_977935512.png",
+ "generator": "wallco.ai",
+ "seed": 977935512,
+ "is_published": false,
+ "created_at": "2026-05-13T19:42:43.439481+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3632,
+ "kind": "seamless_tile",
+ "category": "drunk-animals",
+ "dominant_hex": "#6f6a4d",
+ "saturation": 0.181,
+ "title": "Honey Tableau No.3632",
+ "handle": "wallco-3632",
+ "image_url": "/designs/img/1778701441329_111881294.png",
+ "filename": "1778701441329_111881294.png",
+ "generator": "wallco.ai",
+ "seed": 111881294,
+ "is_published": false,
+ "created_at": "2026-05-13T19:44:10.042957+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
+ },
+ {
+ "id": 3633,
+ "kind": "seamless_tile",
+ "category": "stoned-animals",
+ "dominant_hex": "#292829",
+ "saturation": 0.012,
+ "title": "Noir Daydream No.3633",
+ "handle": "wallco-3633",
+ "image_url": "/designs/img/1778701441479_1026850218.png",
+ "filename": "1778701441479_1026850218.png",
+ "generator": "wallco.ai",
+ "seed": 1026850218,
+ "is_published": false,
+ "created_at": "2026-05-13T19:44:13.069861+00:00",
+ "motifs": [],
+ "user_removed": false,
+ "user_color_good": null,
+ "user_style_good": null,
+ "user_scale_good": null,
+ "user_stars": null,
+ "room_mockups": []
}
]
\ No newline at end of file
diff --git a/server.js b/server.js
index 3662dac..ed4330d 100644
--- a/server.js
+++ b/server.js
@@ -5527,10 +5527,11 @@ ${htmlHeader('/designs')}
</script>
` : ''}
+ ${_isAdmin ? `
<!-- ── USER RATING PANEL ─────────────────────────────────────────
Feeds spoon_all_designs.user_color_good / style_good / scale_good /
- stars / removed. Localhost-admin auto-allowed. Aggregated counts
- will tune future prompt generation. -->
+ stars / removed. Admin-only — POST handler is admin-gated, so
+ rendering for non-admins guarantees "save error" on every click. -->
<div id="rating-panel" data-design-id="${design.id}"
style="margin:18px 0;padding:14px 16px;background:var(--card-bg,#f4f0e8);border:1px solid var(--line,#d8d0c0);border-radius:8px;font:13px var(--sans,system-ui)">
<div style="display:flex;flex-wrap:wrap;gap:14px;align-items:center;justify-content:space-between">
@@ -5649,6 +5650,7 @@ ${htmlHeader('/designs')}
});
})();
</script>
+ ` : ''}
<div class="scale-preview" style="margin-top:18px">
<div class="scale-controls" style="display:flex;align-items:center;gap:14px;margin-bottom:10px;flex-wrap:wrap">
@@ -7327,13 +7329,83 @@ ${htmlHeader('/designs')}
<!-- Generated Rooms gallery — hydrates from /api/rooms?design_id={{id}} -->
<section id="rooms-gallery" class="rooms-gallery" data-design-id="${design.id}" style="margin:48px auto 0;max-width:1200px;padding:0 24px;">
- <div class="rg-head" style="display:flex;align-items:baseline;gap:16px;border-top:1px solid var(--rule,#e5e5e0);padding-top:24px;margin-bottom:18px;">
+ <div class="rg-head" style="display:flex;align-items:baseline;gap:16px;border-top:1px solid var(--rule,#e5e5e0);padding-top:24px;margin-bottom:18px;flex-wrap:wrap;">
<h2 style="margin:0;font:500 22px/1.2 ui-serif,Georgia,serif;">Generated rooms</h2>
<span id="rg-count" style="font:11px/1.4 ui-sans-serif,system-ui,sans-serif;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-faint,#6e6e68);"></span>
<span id="rg-admin-badge" style="display:none;margin-left:auto;font:10px/1.4 ui-sans-serif,system-ui,sans-serif;letter-spacing:.14em;text-transform:uppercase;color:#b00020;">admin mode</span>
</div>
+ ${_isAdmin ? `
+ <!-- See in Room generator — admin-only because it hits the upstream
+ renderer at ROOM_API ($/call). Triggers POST /api/room and dispatches
+ wallco:room-generated so the gallery below refreshes in place. -->
+ <div id="rg-gen-bar" style="display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-bottom:18px;padding:12px 14px;background:var(--card-bg,#f4f0e8);border:1px solid var(--rule,#e5e5e0);border-radius:8px;font:13px ui-sans-serif,system-ui,sans-serif;">
+ <strong style="font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-faint,#7a6e5a)">See in room</strong>
+ <label style="display:flex;align-items:center;gap:6px;font-size:11px;color:var(--ink-faint,#7a6e5a);">
+ Room
+ <select id="rg-room-type" style="font:12px ui-sans-serif,system-ui,sans-serif;padding:4px 8px;border:1px solid var(--line,#d8d0c0);background:#fff;border-radius:4px;">
+ <option value="bedroom">Bedroom</option>
+ <option value="living_room">Living room</option>
+ <option value="dining_room">Dining room</option>
+ <option value="office">Office</option>
+ <option value="bathroom">Bathroom</option>
+ </select>
+ </label>
+ <label style="display:flex;align-items:center;gap:6px;font-size:11px;color:var(--ink-faint,#7a6e5a);">
+ Angle
+ <select id="rg-angle" style="font:12px ui-sans-serif,system-ui,sans-serif;padding:4px 8px;border:1px solid var(--line,#d8d0c0);background:#fff;border-radius:4px;">
+ <option value="straight_on">Straight on</option>
+ <option value="three_quarter">3/4 view</option>
+ <option value="wide">Wide</option>
+ </select>
+ </label>
+ <button id="rg-generate-btn" type="button"
+ style="background:#1f1808;border:1px solid #1f1808;color:#fff;padding:6px 14px;border-radius:14px;cursor:pointer;font-size:12px;letter-spacing:.06em;text-transform:uppercase;">
+ Generate Room
+ </button>
+ <span id="rg-generate-status" style="font-size:11px;color:var(--ink-faint,#888);margin-left:auto;"></span>
+ </div>
+ <script>
+ (function(){
+ var btn = document.getElementById('rg-generate-btn');
+ var statusEl = document.getElementById('rg-generate-status');
+ var roomTypeSel = document.getElementById('rg-room-type');
+ var angleSel = document.getElementById('rg-angle');
+ var designId = document.getElementById('rooms-gallery').dataset.designId;
+ btn.addEventListener('click', function(){
+ btn.disabled = true;
+ var originalLabel = btn.textContent;
+ btn.textContent = 'Generating…';
+ statusEl.textContent = 'rendering room (15-30s)…';
+ fetch('/api/room', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ credentials: 'same-origin',
+ body: JSON.stringify({
+ design_id: parseInt(designId, 10),
+ roomType: roomTypeSel.value,
+ angle: angleSel.value
+ })
+ }).then(function(r){
+ return r.json().then(function(j){ return { ok: r.ok, j: j }; });
+ }).then(function(wrap){
+ if (!wrap.ok || wrap.j.ok === false) {
+ throw new Error((wrap.j && wrap.j.error) || ('http ' + (wrap.j.status || '?')));
+ }
+ statusEl.textContent = 'saved · refreshing gallery';
+ window.dispatchEvent(new CustomEvent('wallco:room-generated'));
+ setTimeout(function(){ statusEl.textContent = ''; }, 2000);
+ }).catch(function(e){
+ statusEl.textContent = 'error: ' + e.message;
+ }).then(function(){
+ btn.disabled = false;
+ btn.textContent = originalLabel;
+ });
+ });
+ })();
+ </script>
+ ` : ''}
<div id="rg-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px;"></div>
- <div id="rg-empty" style="display:none;padding:32px;text-align:center;color:var(--ink-faint,#6e6e68);font-style:italic;">No rooms generated for this pattern yet. Use the "Generate Room" button above to create one.</div>
+ <div id="rg-empty" style="display:none;padding:32px;text-align:center;color:var(--ink-faint,#6e6e68);font-style:italic;">${_isAdmin ? 'No rooms generated for this pattern yet. Use the "Generate Room" button above to create one.' : 'No rooms generated for this pattern yet.'}</div>
</section>
</main>
${FOOTER}
← 39b4997 api/designs: filter out rows whose image file is missing on
·
back to Wallco Ai
·
package: pin multer dep (wallco prod kept losing it on npm c 05ab37b →