[object Object]

← back to Ticket System

Add draggable ticket status tabs

29ce97b117fecc934910db173572af44fb3f9708 · 2026-09-02 15:09:05 -0700 · Steve Abrams

Files touched

Diff

commit 29ce97b117fecc934910db173572af44fb3f9708
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 2 15:09:05 2026 -0700

    Add draggable ticket status tabs
---
 board.html | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/board.html b/board.html
index 43f35bd2..0390c2d8 100644
--- a/board.html
+++ b/board.html
@@ -106,7 +106,7 @@
 </style></head><body>
 <header>
   <h1>FLEET TICKETS</h1>
-  <nav class="globalnav" aria-label="Fleet viewer"><button data-section="all">All</button><button data-section="blocked">Blocked</button><button data-section="agents">Agents</button><button data-section="skills">Skills</button></nav>
+  <nav class="globalnav" aria-label="Fleet viewer"><button data-section="all">All</button><button data-section="open">Open</button><button data-section="blocked">Blocked</button><button data-section="doing">Doing</button><button data-section="agents">Agents</button><button data-section="skills">Skills</button></nav>
   <select class="gsel" id="view" title="Separate actionable work from recurring registries">
     <option value="task">Task Queue</option><option value="continuous_loop">Continuous Loops</option><option value="scheduled_job">Scheduled Jobs</option><option value="all">All</option>
   </select>
@@ -208,7 +208,7 @@ const LS=(k,d)=>{try{return JSON.parse(localStorage.getItem(k))??d}catch(e){retu
 let DATA=[], AGENTS=[], SKILLS=[], q='';
 const URLSTATE=new URLSearchParams(location.search);
 let SECTION=URLSTATE.get('section')||LS('tkSection','blocked'), LAYOUT=URLSTATE.get('layout')||LS('tkLayout','grid');
-if(!['all','blocked','agents','skills'].includes(SECTION))SECTION='blocked';if(!['grid','kv'].includes(LAYOUT))LAYOUT='grid';
+if(!['all','open','blocked','doing','agents','skills'].includes(SECTION))SECTION='blocked';if(!['grid','kv'].includes(LAYOUT))LAYOUT='grid';
 let CATALOG_SORT=LS('tkCatalogSort','name'), CATALOG_DIR=LS('tkCatalogDir',1);
 let sortKey=LS('tkSortKey','created_at'), sortDir=LS('tkSortDir',-1); if(!CK.includes(sortKey))sortKey='created_at';
 let VIEW=LS('tkView','task');
@@ -258,7 +258,7 @@ function renderHead(){
   }
   $('#selall').onclick=e=>{const on=e.target.checked;const vis=DATA.filter(match);if(on)for(const t of vis)SEL.add(t.id);else for(const t of vis)SEL.delete(t.id);render();syncBar();};
 }
-function match(t){if(VIEW!=='all'&&(t.kind||'task')!==VIEW)return false;if(SECTION==='blocked'&&t.status!=='blocked')return false;if(!q)return true;
+function match(t){if(VIEW!=='all'&&(t.kind||'task')!==VIEW)return false;if(['open','blocked','doing'].includes(SECTION)&&t.status!==SECTION)return false;if(!q)return true;
   // search the FULL thread — every comment (note/win/challenge/cody incl.) + every action — not just lastText
   const thread=[...(t.comments||[]).map(c=>c.text),...(t.actions||[]).map(a=>a.text)].join(' ');
   const hay=(t.id+' '+t.title+' '+(t.project||'')+' '+(t.assignee||'')+' '+t.status+' '+(t.kind||'task')+' '+JSON.stringify(t.schedule||{})+' '+thread).toLowerCase();
@@ -324,6 +324,17 @@ function renderCatalog(){
 }
 function syncUrl(){const u=new URL(location.href);u.searchParams.set('section',SECTION);u.searchParams.set('layout',LAYOUT);history.replaceState(null,'',u);}
 function setSection(section){SECTION=section;save('tkSection',SECTION);syncUrl();q='';$('#q').value='';document.querySelectorAll('[data-section]').forEach(b=>b.classList.toggle('on',b.dataset.section===SECTION));render();}
+function initSectionTabs(){
+  const nav=$('.globalnav'),keys=['all','open','blocked','doing','agents','skills'];
+  let order=LS('tkSectionOrder',keys).filter(k=>keys.includes(k));for(const k of keys)if(!order.includes(k))order.push(k);
+  const apply=()=>{for(const k of order){const b=nav.querySelector(`[data-section="${k}"]`);if(b)nav.appendChild(b);}};apply();
+  nav.querySelectorAll('[data-section]').forEach(b=>{b.draggable=true;b.title='Drag to reorder tabs';
+    b.ondragstart=e=>{e.dataTransfer.setData('text/plain',b.dataset.section);e.dataTransfer.effectAllowed='move';};
+    b.ondragover=e=>{e.preventDefault();e.dataTransfer.dropEffect='move';};
+    b.ondrop=e=>{e.preventDefault();const from=e.dataTransfer.getData('text/plain'),to=b.dataset.section;if(!from||from===to)return;order=order.filter(k=>k!==from);order.splice(order.indexOf(to),0,from);save('tkSectionOrder',order);apply();};
+  });
+}
+initSectionTabs();
 function setLayout(layout){LAYOUT=layout;save('tkLayout',LAYOUT);syncUrl();document.querySelectorAll('[data-layout]').forEach(b=>b.classList.toggle('on',b.dataset.layout===LAYOUT));if(SECTION==='agents'||SECTION==='skills')renderCatalog();else{const agent=window.NavAgent&&window.NavAgent.instances&&window.NavAgent.instances[0];if(agent&&agent.setView)agent.setView(LAYOUT==='kv'?'list':'grid');}}
 document.querySelectorAll('[data-section]').forEach(b=>b.onclick=()=>setSection(b.dataset.section));document.querySelectorAll('[data-layout]').forEach(b=>b.onclick=()=>setLayout(b.dataset.layout));
 document.querySelectorAll('[data-section]').forEach(b=>b.classList.toggle('on',b.dataset.section===SECTION));document.querySelectorAll('[data-layout]').forEach(b=>b.classList.toggle('on',b.dataset.layout===LAYOUT));

← 2d7b8714 Add centered ticket control popup  ·  back to Ticket System  ·  Authenticate ticket popup API session 5714f06a →