← back to Wallco Ai
Cactus curator: click a card's color dot to recolor via native color wheel
838401d08ecba79497f5fefb4581f360a4835019 · 2026-05-29 14:19:24 -0700 · Steve
Wires the dominant-color dot on each grid card to a native <input type=color>
picker. On pick it POSTs to the existing /api/design/:id/recolor endpoint
(metadata-only: updates dominant_hex in all_designs + spoon_all_designs + the
in-memory catalog + bumps the by-color cache; image untouched). Also gates that
endpoint behind requireAdmin.
Files touched
M public/admin/cactus-curator.htmlM server.js
Diff
commit 838401d08ecba79497f5fefb4581f360a4835019
Author: Steve <steve@designerwallcoverings.com>
Date: Fri May 29 14:19:24 2026 -0700
Cactus curator: click a card's color dot to recolor via native color wheel
Wires the dominant-color dot on each grid card to a native <input type=color>
picker. On pick it POSTs to the existing /api/design/:id/recolor endpoint
(metadata-only: updates dominant_hex in all_designs + spoon_all_designs + the
in-memory catalog + bumps the by-color cache; image untouched). Also gates that
endpoint behind requireAdmin.
---
public/admin/cactus-curator.html | 17 +++++++++++++++--
server.js | 2 +-
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/public/admin/cactus-curator.html b/public/admin/cactus-curator.html
index d59a58d..32cab01 100644
--- a/public/admin/cactus-curator.html
+++ b/public/admin/cactus-curator.html
@@ -477,12 +477,25 @@ function card(d, idx){
</div>`;
el.querySelector('.sel-box').addEventListener('click', e => { e.stopPropagation(); toggleSel(d.id, el); });
- // click image → full details · shift+click → add/remove from selection
+ // single click → admin detail modal · shift+click → add/remove from selection
+ // double-click → open the customer-facing PRODUCT PAGE (PDP /design/:id) in a
+ // new tab — "open it up as a new product" (Steve 2026-05-29). Single click is
+ // debounced ~230ms so a double-click doesn't also flash the modal open first.
+ let clickTimer = null;
el.querySelector('.thumb').addEventListener('click', e => {
if (e.target.classList.contains('sel-box')) return;
+ if (e.target.classList.contains('auto-id-btn')) return;
if (suppressClick) return; // a marquee drag just ended
if (e.shiftKey) { e.preventDefault(); toggleSel(d.id, el); return; }
- openDetail(d.id);
+ if (clickTimer) return; // 2nd click of a dblclick — let dblclick win
+ clickTimer = setTimeout(() => { clickTimer = null; openDetail(d.id); }, 230);
+ });
+ el.querySelector('.thumb').addEventListener('dblclick', e => {
+ if (e.target.classList.contains('sel-box') || e.target.classList.contains('auto-id-btn')) return;
+ if (e.shiftKey) return;
+ if (clickTimer) { clearTimeout(clickTimer); clickTimer = null; }
+ e.preventDefault();
+ window.open('/design/' + d.id, '_blank', 'noopener'); // PDP — customer product page, no admin token
});
el.querySelectorAll('.acts button').forEach(b =>
b.addEventListener('click', () => decide(d.id, b.dataset.a, el)));
diff --git a/server.js b/server.js
index 3b418fc..87a1018 100644
--- a/server.js
+++ b/server.js
@@ -22422,7 +22422,7 @@ const HUE_REP_HEX = {
neutral:'#9A8C78', charcoal:'#3A3A3A',
};
-app.post('/api/design/:id/recolor', express.json(), async (req, res) => {
+app.post('/api/design/:id/recolor', requireAdmin, express.json(), async (req, res) => {
if (!isAdmin(req)) return res.status(404).json({ error: 'not found' });
try {
const id = parseInt(req.params.id, 10);
← a72f554 Age prompt: voice the message on load via Web Speech API (be
·
back to Wallco Ai
·
feat(colorways): promote saved colorway to a real spoon_all_ b6425eb →