[object Object]

← back to Wallco Ai

designs grid: add 'Digital for Sale' bulk action (unpublish + tag digital-file + stamp digital_file_at)

333f44d4b0afeca370481665aa382dcdfbd42e89 · 2026-06-03 14:07:36 -0700 · Steve Abrams

Files touched

Diff

commit 333f44d4b0afeca370481665aa382dcdfbd42e89
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jun 3 14:07:36 2026 -0700

    designs grid: add 'Digital for Sale' bulk action (unpublish + tag digital-file + stamp digital_file_at)
---
 server.js | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/server.js b/server.js
index b3657db..3a887a8 100644
--- a/server.js
+++ b/server.js
@@ -4833,7 +4833,7 @@ app.post('/api/designs/bulk-action', express.json({ limit: '64kb' }), (req, res)
   const ids = Array.isArray(req.body?.ids) ? req.body.ids.map((x) => parseInt(x, 10)).filter((n) => Number.isFinite(n) && n > 0) : [];
   const action = String(req.body?.action || '').toLowerCase();
   if (!ids.length) return res.status(400).json({ error: 'ids[] required' });
-  if (!['delete', 'publish', 'unpublish', 'tag'].includes(action)) return res.status(400).json({ error: 'unknown action' });
+  if (!['delete', 'publish', 'unpublish', 'digital', 'tag'].includes(action)) return res.status(400).json({ error: 'unknown action' });
   const idList = ids.join(',');
   try {
     let affected = 0;
@@ -4851,6 +4851,23 @@ app.post('/api/designs/bulk-action', express.json({ limit: '64kb' }), (req, res)
     } else if (action === 'unpublish') {
       psqlExecLocal(`UPDATE spoon_all_designs SET is_published=FALSE WHERE id IN (${idList});`);
       affected = ids.length;
+    } else if (action === 'digital') {
+      // Unpublish from the wallpaper catalog AND mark as a digital file for sale.
+      // Mirrors the proven cactus-curator 'digital' verdict (lines ~3018): off
+      // the live catalog (is_published/web_viewer=FALSE), stamp digital_file_at,
+      // and tag 'digital-file' so the digital-pack pipeline picks it up.
+      // Written against the base table all_designs (the columns digital_file_at /
+      // web_viewer / tags live there) — same target the cactus path uses.
+      psqlExecLocal(`UPDATE all_designs
+        SET is_published=FALSE, web_viewer=FALSE, digital_file_at=now(),
+            tags = array_remove(COALESCE(tags, ARRAY[]::text[]), 'digital-file') || ARRAY['digital-file']::text[]
+        WHERE id IN (${idList});`);
+      affected = ids.length;
+      // Drop from in-memory catalog so it leaves the live feed immediately.
+      for (const id of ids) {
+        const idx = DESIGNS.findIndex((x) => x.id === id);
+        if (idx >= 0) DESIGNS.splice(idx, 1);
+      }
     } else if (action === 'tag') {
       // For now — accept but don't implement tag-write path (needs UI for tag input)
       return res.status(501).json({ error: 'tag action not yet implemented — coming soon' });
@@ -8500,6 +8517,7 @@ ${(req.query.source === 'all') ? `
     <button type="button" data-bulk="tag"      style="background:#ddc28a;color:#1a1816;border:0;padding:6px 14px;border-radius:999px;cursor:pointer;font-weight:600">Tag…</button>
     <button type="button" data-bulk="publish"  style="background:#5a8a5a;color:#fff;border:0;padding:6px 14px;border-radius:999px;cursor:pointer;font-weight:600">Publish</button>
     <button type="button" data-bulk="unpublish" style="background:#8a7a5a;color:#fff;border:0;padding:6px 14px;border-radius:999px;cursor:pointer;font-weight:600">Unpublish</button>
+    <button type="button" data-bulk="digital"  style="background:#6a5acd;color:#fff;border:0;padding:6px 14px;border-radius:999px;cursor:pointer;font-weight:600" title="Unpublish from the wallpaper catalog and mark as a digital file for sale (tags digital-file, stamps digital_file_at)">💾 Digital for Sale</button>
     <button type="button" data-bulk="delete"   style="background:#b04030;color:#fff;border:0;padding:6px 14px;border-radius:999px;cursor:pointer;font-weight:600">Delete</button>
     <button type="button" data-bulk="contactsheet" style="background:#3a6ea5;color:#fff;border:0;padding:6px 14px;border-radius:999px;cursor:pointer;font-weight:600" title="Build a downloadable contact sheet PNG of the selected designs">📇 Contact sheet</button>
     <button type="button" data-bulk="cancel"   style="background:transparent;color:#fff;border:1px solid rgba(255,255,255,.3);padding:6px 12px;border-radius:999px;cursor:pointer">Cancel</button>
@@ -8755,7 +8773,7 @@ ${(req.query.source === 'all') ? `
       if (action === 'cancel') { clearSelection(); return; }
       if (action === 'contactsheet') { buildContactSheet(Array.from(selected), btn); return; }
       var ids = Array.from(selected); if (!ids.length) return;
-      var labels = { tag:'Tag', publish:'Publish', unpublish:'Unpublish', 'delete':'Delete' };
+      var labels = { tag:'Tag', publish:'Publish', unpublish:'Unpublish', digital:'Add Digital for Sale', 'delete':'Delete' };
       if (!confirm(labels[action] + ' ' + ids.length + ' design(s)?')) return;
       btn.disabled = true; btn.style.opacity = '.6';
       // Build absolute URL via origin-without-credentials so Chrome doesn't refuse:
@@ -8771,7 +8789,7 @@ ${(req.query.source === 'all') ? `
         btn.disabled = false; btn.style.opacity = '1';
         if (j && j.ok) {
           alert(labels[action] + ' applied to ' + (j.affected || ids.length) + ' design(s).');
-          if (action === 'delete') ids.forEach(function(id){ var c = grid.querySelector('[data-design-id="' + id + '"]'); if (c) c.remove(); });
+          if (action === 'delete' || action === 'digital') ids.forEach(function(id){ var c = grid.querySelector('[data-design-id="' + id + '"]'); if (c) c.remove(); });
           clearSelection();
         } else { alert('Bulk action failed: ' + (j && j.error || 'unknown')); }
       }).catch(function(err){ btn.disabled = false; btn.style.opacity = '1'; alert('Network error: ' + err.message); });

← 8282219 dogs-curator: fix greedy edges parse (split_part slurped app  ·  back to Wallco Ai  ·  build-mural-master: retry per-tile ESRGAN calls (4x backoff) bd8def2 →