← back to Approvals Viewer
approvals-viewer: fix blank page — nested-quote SyntaxError in inline handlers
a0197a431a388d7dbd03358673349f0624831893 · 2026-07-28 09:21:59 -0700 · Steve Abrams
The card action buttons + memo toggle used single-quoted inline onclick handlers
inside single-quoted JS strings; the escaped quotes collapsed and broke the whole
inline script (page stuck on 'loading…'). Switched action buttons to
data-attributes + one delegated click listener; memo toggle to a named tog().
Files touched
Diff
commit a0197a431a388d7dbd03358673349f0624831893
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jul 28 09:21:59 2026 -0700
approvals-viewer: fix blank page — nested-quote SyntaxError in inline handlers
The card action buttons + memo toggle used single-quoted inline onclick handlers
inside single-quoted JS strings; the escaped quotes collapsed and broke the whole
inline script (page stuck on 'loading…'). Switched action buttons to
data-attributes + one delegated click listener; memo toggle to a named tog().
---
server.js | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/server.js b/server.js
index 8d00b82..b7b6f81 100644
--- a/server.js
+++ b/server.js
@@ -145,12 +145,18 @@ function card(d){
+btn(d,'defer','no','⏸ No / Defer')
+btn(d,'block','blk','⛔ Block')
+'</div>'
- +'<div class=mtoggle onclick="var p=this.nextElementSibling;p.style.display=p.style.display===\'block\'?\'none\':\'block\'">▸ memo</div>'
+ +'<div class=mtoggle onclick="tog(this)">▸ memo</div>'
+'<pre>'+esc(d.body)+'</pre>'
+'</div>';
}
-function btn(d,dec,cls,label){ return '<button class="'+cls+(d.decision===dec?' sel':'')+'" onclick="decide(\''+d.slug+'\',\''+(d.decision===dec?'undecided':dec)+'\')">'+label+'</button>'; }
+function btn(d,dec,cls,label){ return '<button class="'+cls+(d.decision===dec?' sel':'')+'" data-slug="'+d.slug+'" data-dec="'+dec+'">'+label+'</button>'; }
+document.getElementById('list').addEventListener('click',function(e){
+ var b=e.target.closest('button[data-slug]'); if(!b) return;
+ var d=DATA.find(function(x){return x.slug===b.dataset.slug;}); var dec=b.dataset.dec;
+ decide(b.dataset.slug,(d&&d.decision===dec)?'undecided':dec);
+});
function esc(s){return String(s).replace(/[&<>]/g,c=>({'&':'&','<':'<','>':'>'}[c]));}
+function tog(el){var p=el.nextElementSibling;p.style.display=p.style.display==="block"?"none":"block";}
async function decide(slug,decision){
await fetch('/api/decide',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({slug,decision})});
const d=DATA.find(x=>x.slug===slug); if(d) d.decision=decision; render();
← ecc4f6c approvals-viewer: skip auth for loopback (fixes blank page)
·
back to Approvals Viewer
·
add top-10 fan-out rollup board (:9793) tracking TK-10012 ta 9ff4214 →