[object Object]

← back to Wallco Ai

luxe-curator: add canonical .when chip + flow created_at through /api/luxe-curator

7e3422988b33348b11517f1a674f553cb5b2cc37 · 2026-05-28 13:33:40 -0700 · Steve Abrams

Front-end (public/luxe-curator.html):
- Add .when chip (bottom-left of root tile) mirroring cactus-curator pattern:
  🕓 ${fmtDate(p.created_at)} + ISO timestamp in title=.
- Add local fmtDate() helper (full year/month/day/hour/minute via toLocaleString).
- Add .when CSS rule (paired with the existing .ground bottom-right chip).

Server (server.js /api/luxe-curator):
- Capture the earliest log ts per root as created_at on the row object
  (regen entries should not bump created_at forward).
- Include created_at in the response payload.

Closes tick-37 audit finding for luxe-curator (was the only "missing" surface;
"server.js change needed first" blocker resolved).

Files touched

Diff

commit 7e3422988b33348b11517f1a674f553cb5b2cc37
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu May 28 13:33:40 2026 -0700

    luxe-curator: add canonical .when chip + flow created_at through /api/luxe-curator
    
    Front-end (public/luxe-curator.html):
    - Add .when chip (bottom-left of root tile) mirroring cactus-curator pattern:
      🕓 ${fmtDate(p.created_at)} + ISO timestamp in title=.
    - Add local fmtDate() helper (full year/month/day/hour/minute via toLocaleString).
    - Add .when CSS rule (paired with the existing .ground bottom-right chip).
    
    Server (server.js /api/luxe-curator):
    - Capture the earliest log ts per root as created_at on the row object
      (regen entries should not bump created_at forward).
    - Include created_at in the response payload.
    
    Closes tick-37 audit finding for luxe-curator (was the only "missing" surface;
    "server.js change needed first" blocker resolved).
---
 public/luxe-curator.html | 12 ++++++++++++
 server.js                | 10 +++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/public/luxe-curator.html b/public/luxe-curator.html
index 5aba62d..e85f761 100644
--- a/public/luxe-curator.html
+++ b/public/luxe-curator.html
@@ -55,6 +55,11 @@
     letter-spacing:0.05em; background:rgba(255,255,255,0.92); color:#1a1a1a;
     border-radius:2px; font-weight:600;
   }
+  .row .tile .when {
+    position:absolute; bottom:6px; left:6px; padding:2px 6px; font-size:9.5px;
+    background:rgba(255,255,255,0.92); color:#1a1a1a; border-radius:2px;
+    font-weight:600; font-variant-numeric:tabular-nums; letter-spacing:.01em;
+  }
   .row .tile .regen-btn {
     position:absolute; top:6px; right:6px; width:24px; height:24px;
     border-radius:50%; background:rgba(255,255,255,0.92); border:none;
@@ -116,6 +121,12 @@ async function loadData() {
   return r.json();
 }
 
+function fmtDate(iso) {
+  if (!iso) return '—';
+  const d = new Date(iso); if (isNaN(d)) return '—';
+  return d.toLocaleString(undefined, { year:'numeric', month:'short', day:'numeric', hour:'numeric', minute:'2-digit' });
+}
+
 function toast(msg) {
   const t = document.getElementById('toast');
   t.textContent = msg;
@@ -146,6 +157,7 @@ function render() {
         <div class="tile" data-pick="root" data-row="${i}" title="Keep root (don't publish any variant)">
           <img loading="lazy" src="/designs/img/by-id/${p.root_id}?asis=1" alt="root ${p.root_id}">
           <span class="label root">Root #${p.root_id}</span>
+          <span class="when" title="queued ${p.created_at||''}">🕓 ${fmtDate(p.created_at)}</span>
         </div>
         ${variantTiles}
         <div class="meta">
diff --git a/server.js b/server.js
index d55e522..4026c05 100644
--- a/server.js
+++ b/server.js
@@ -18186,13 +18186,20 @@ app.get('/api/luxe-curator', (req, res) => {
       try {
         const r = JSON.parse(line);
         if (!r.ok || !r.new_id) continue;
-        if (!byRoot.has(r.root_id)) byRoot.set(r.root_id, { root_id: r.root_id, motif: r.motif, variantMap: new Map() });
+        if (!byRoot.has(r.root_id)) byRoot.set(r.root_id, { root_id: r.root_id, motif: r.motif, variantMap: new Map(), created_at: r.ts || null });
         // Overwrite previous entry for this variant — latest wins
         byRoot.get(r.root_id).variantMap.set(r.variant, { variant: r.variant, new_id: r.new_id, ground: r.ground, duration_ms: r.duration_ms });
         // Keep the longest motif we've seen — regen entries may have a truncated one
         if (r.motif && r.motif.length > (byRoot.get(r.root_id).motif || '').length) {
           byRoot.get(r.root_id).motif = r.motif;
         }
+        // Keep the EARLIEST ts as the root's created_at — the moment this
+        // root was first queued for luxe-curator processing (regen entries
+        // are later by definition and should not bump created_at forward).
+        if (r.ts) {
+          const cur = byRoot.get(r.root_id).created_at;
+          if (!cur || r.ts < cur) byRoot.get(r.root_id).created_at = r.ts;
+        }
       } catch {}
     }
     let decided = new Set();
@@ -18205,6 +18212,7 @@ app.get('/api/luxe-curator', (req, res) => {
       .map(r => ({
         root_id: r.root_id,
         motif: r.motif,
+        created_at: r.created_at,
         variants: [...r.variantMap.values()].sort((a, b) => a.variant.localeCompare(b.variant)),
       }))
       .sort((a, b) => a.root_id - b.root_id);

← e1b59d8 wallco.ai downloads route: /downloads/:slug/:token.zip token  ·  back to Wallco Ai  ·  strip Scenic/Studio sub-brand from customer-facing surfaces aed2aa0 →