[object Object]

← back to Re Flyer Aggregator

sources dashboard: expanded article list now renders UNDER the Recent column (right side), stacked directly below the preview articles, text justified + wrapping (Steve) (TK-10708)

fe4e646892d92fb83b1f05e1f9a85e10085c265a · 2026-08-20 00:03:04 -0700 · Steve Abrams

Files touched

Diff

commit fe4e646892d92fb83b1f05e1f9a85e10085c265a
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Aug 20 00:03:04 2026 -0700

    sources dashboard: expanded article list now renders UNDER the Recent column (right side), stacked directly below the preview articles, text justified + wrapping (Steve) (TK-10708)
---
 public/sources/index.html | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/public/sources/index.html b/public/sources/index.html
index 3f8cc7e..ced51ac 100644
--- a/public/sources/index.html
+++ b/public/sources/index.html
@@ -64,6 +64,9 @@
  td.num{cursor:pointer} td.num:hover{text-decoration:underline}
  tr.detail>td{background:#f7f9ff;padding:6px 14px 12px} .dwrap{font-size:12px;color:#445} .dwrap .dm{color:#6B7280;margin-bottom:6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .dwrap .ds,.samp>div{padding:3px 0;white-space:nowrap} .dwrap .ds{border-top:1px solid #eef1f8} .dwrap .ds a{color:#16357A;text-decoration:none} .samp>div{padding:1px 0} td.samp{min-width:340px}
+ /* expanded detail articles: stacked under the Recent column, justified, wrapping */
+ td.detarts{vertical-align:top;padding:4px 12px 12px} td.detarts .ds,.dwrap.just .ds{white-space:normal;text-align:justify;padding:4px 0;border-top:1px solid #eef1f8;line-height:1.5}
+ td.detarts .ds a,.dwrap.just .ds a{color:#16357A;text-decoration:none}
  #nav .col{display:flex;justify-content:space-between;align-items:center;padding:6px 14px;cursor:pointer;font-size:13px;color:#445}
  #nav .col:hover{background:#eef1f8} #nav .col .sw{width:30px;height:16px;border-radius:9px;background:#ccd3e6;position:relative;transition:.15s;flex:none}
  #nav .col.on .sw{background:#0E7C4A} #nav .col .sw::after{content:"";position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:#fff;transition:.15s} #nav .col.on .sw::after{left:16px}
@@ -175,10 +178,13 @@ function cellHTML(x,k,max){
  if(k==='latest')return `<td style="white-space:nowrap;color:#8A5A00">${latestOf(x)?dt(new Date(latestOf(x)).toISOString()):'—'}</td>`;
  if(k==='recent')return `<td class="samp">${sortSamp(x.samples).slice(0,3).map(s=>`<div><span style="color:#8A5A00">${dt(s.date)}</span> ${s.price?'<b>'+esc(s.price)+'</b> ':''}<a href="${s.link}" target="_blank" rel="noopener" onclick="event.stopPropagation()">${esc(s.title)}</a> <span style="color:#9aa">· ${esc(s.market||'')}</span></div>`).join('')||'<span style="color:#c3c9d6">—</span>'}</td>`;
  return '<td></td>';}
-function detailRow(x,span){
- return `<tr class="detail"><td colspan="${span}"><div class="dwrap">
-  <div class="dm"><b>${esc(x.name)}</b> · ${x.method} · ${esc(x.region)} · pulled ${x.pulled} · kept ${x.deals||0}${x.url?` · <a href="${esc(x.url)}" target="_blank" rel="noopener">${esc(x.url.slice(0,64))}</a>`:''}</div>
-  ${(x.samples||[]).slice().sort((a,b)=>(Date.parse(b.date)||0)-(Date.parse(a.date)||0)).map(s=>`<div class="ds"><span style="color:#8A5A00">${dt(s.date)}</span> ${s.price?'<b>'+esc(s.price)+'</b> ':''}<a href="${s.link}" target="_blank" rel="noopener">${esc(s.title)}</a> <span style="color:#9aa">· ${esc(s.market||'')}</span></div>`).join('')||'<div class="ds" style="color:#9aa">no deal samples</div>'}</div></td></tr>`;}
+function detailRow(x){
+ const ck=cols();
+ const arts=sortSamp(x.samples).map(s=>`<div class="ds"><span style="color:#8A5A00">${dt(s.date)}</span> ${s.price?'<b>'+esc(s.price)+'</b> ':''}<a href="${s.link}" target="_blank" rel="noopener">${esc(s.title)}</a> <span style="color:#9aa">· ${esc(s.market||'')}</span></div>`).join('')||'<div class="ds" style="color:#9aa">no more articles</div>';
+ // place the full article list DIRECTLY UNDER the "Recent" column (right side), justified.
+ if(!ck.includes('recent')) return `<tr class="detail"><td class="xc"></td><td colspan="${ck.length}"><div class="dwrap just">${arts}</div></td></tr>`;
+ const cells=ck.map(k=>k==='recent'?`<td class="samp detarts">${arts}</td>`:'<td></td>').join('');
+ return `<tr class="detail"><td class="xc"></td>${cells}</tr>`;}
 function buildNav(){ if(!SRC)return; let h='';
  for(const [key,lbl] of NAVFIELDS){ const counts={}; SRC.feeds.forEach(x=>{const v=x[key]||'—';counts[v]=(counts[v]||0)+1;});
   h+=`<div class="field">${lbl}</div>`;
@@ -225,7 +231,7 @@ function renderSrc(){ if(!SRC)return; const f=feedsFiltered();
  if(VIEW==='table'){ tbl.style.display=''; cards.style.display='none';
   const max=Math.max(1,...SRC.feeds.map(x=>x.pulled)); const ck=cols();
   document.getElementById('tb').innerHTML=f.map(x=>{ const cells=ck.map(k=>cellHTML(x,k,max)).join('');
-   return `<tr class="srow${EXP.has(x.name)?' exp':''}" data-n="${esc(x.name)}"><td class="xc"><span class="car">${EXP.has(x.name)?'▾':'▸'}</span></td>${cells}</tr>`+(EXP.has(x.name)?detailRow(x,ck.length+1):''); }).join('');
+   return `<tr class="srow${EXP.has(x.name)?' exp':''}" data-n="${esc(x.name)}"><td class="xc"><span class="car">${EXP.has(x.name)?'▾':'▸'}</span></td>${cells}</tr>`+(EXP.has(x.name)?detailRow(x):''); }).join('');
  } else { tbl.style.display='none'; cards.style.display=''; cards.className=VIEW; cards.innerHTML=f.map(cardHTML).join(''); }
  const fil=(F.method||F.region)?` · filtered ${F.method||''} ${F.region||''}`:'';
  document.getElementById('sstat').textContent=`${f.length} of ${SRC.feeds.length} sources${fil} · ${VIEW}`;

← f819c87 news: add --deep 90-day backfill mode (Codex-validated) — gn  ·  back to Re Flyer Aggregator  ·  news: remove pulled_list/samples storage cap (was 250) -> In e28d009 →