diff --git a/projects/msp-tools/bug-tracker/dashboard.html b/projects/msp-tools/bug-tracker/dashboard.html index 03cc08e1..0cfd84e6 100644 --- a/projects/msp-tools/bug-tracker/dashboard.html +++ b/projects/msp-tools/bug-tracker/dashboard.html @@ -197,19 +197,17 @@ const SECTIONS = [ { id: 'rmm-bugs', label: 'RMM Bugs', repos: ['gururmm','guru-rmm'], filter: i => getType(i)==='bug' }, { id: 'rmm-features', label: 'RMM Features', repos: ['gururmm','guru-rmm'], filter: i => getType(i)==='feature' }, { id: 'rmm-security', label: 'RMM Security', repos: ['gururmm','guru-rmm'], filter: i => getType(i)==='security' }, - { id: 'gc-all', label: 'GuruConnect', repos: ['guru-connect'], filter: () => true }, + { id: 'gc-all', label: 'GuruConnect', repos: ['guru-connect'], filter: i => !isThought(i) }, { id: 'ct-bugs', label: 'Harness / Skill Bugs', repos: ['claudetools'], filter: i => getType(i)==='bug' || getType(i)==='security' }, { id: 'ct-skills', label: 'Skill Work', repos: ['claudetools'], filter: i => getType(i)==='skill' }, - { id: 'ct-projects', label: 'Projects', repos: ['claudetools'], filter: i => (getType(i)==='project' || getType(i)==='feature') && !isClientProject(i) }, + { id: 'ct-projects', label: 'Projects', repos: ['claudetools'], filter: i => (getType(i)==='project' || getType(i)==='feature') && !isClientProject(i) && !isThought(i) }, ]; -// Client project sections — built dynamically from labels -// Client sections are built dynamically from labels — only clients with issues appear in sidebar const CLIENT_SECTIONS = []; const ALL_REPOS = ['gururmm','guru-rmm','guru-connect','claudetools']; const REPO_NAMES = { 'gururmm':'GuruRMM', 'guru-rmm':'GuruRMM', 'guru-connect':'GuruConnect', 'claudetools':'ClaudeTools' }; let allIssues = [], labelCache = {}, currentSection = 'all', currentView = 'board'; -let searchText = '', showClosed = false, sortField = 'updated', sortDir = -1; +let searchText = '', sortField = 'updated', sortDir = -1; // --- Auth --- function getAuth() { return localStorage.getItem('acg_tk') || ''; } @@ -291,6 +289,19 @@ function getType(i) { return null; } function isClientProject(i) { return i.labels.some(l => l.name === 'client-project'); } +function isThought(i) { return i.labels.some(l => l.name === 'thought'); } +function getDueDate(i) { return i.due_date ? i.due_date.split('T')[0] : null; } +function isDueUrgent(i) { + const d = getDueDate(i); + if (!d) return false; + const diff = (new Date(d) - new Date()) / 86400000; + return diff <= 7; +} +function isDueOverdue(i) { + const d = getDueDate(i); + if (!d) return false; + return new Date(d) < new Date(); +} function getAssignee(i) { const a = i.labels.find(l => l.name.startsWith('assigned:')); return a ? a.name.split(':')[1] : null; @@ -317,26 +328,28 @@ function tagStyle(l) { return `background:#${l.color}20;color:#${l.color};`; } function getFiltered() { let items = allIssues; - if (currentSection.startsWith('cp-')) { - // Client project section — dynamic + if (currentSection === 'my-howard') { + items = items.filter(i => getAssignee(i) === 'howard'); + } else if (currentSection === 'my-mike') { + items = items.filter(i => getAssignee(i) === 'mike'); + } else if (currentSection === 'thoughts') { + items = items.filter(i => isThought(i)); + } else if (currentSection.startsWith('cp-')) { items = items.filter(i => isClientProject(i)); if (currentSection !== 'cp-all') { const clientTag = 'client:' + currentSection.slice(3); items = items.filter(i => i.labels.some(l => l.name === clientTag)); } } else if (currentSection === 'all') { - // Exclude client projects from "all issues" - items = items.filter(i => !isClientProject(i)); + items = items.filter(i => !isClientProject(i) && !isThought(i)); } else if (currentSection !== 'activity') { const sec = SECTIONS.find(s=>s.id===currentSection); - if (sec) items = items.filter(i => sec.repos.includes(i._repo) && sec.filter(i) && !isClientProject(i)); + if (sec) items = items.filter(i => sec.repos.includes(i._repo) && sec.filter(i) && !isClientProject(i) && !isThought(i)); } if (searchText) { const q = searchText.toLowerCase(); items = items.filter(i => i.title.toLowerCase().includes(q) || (i.body||'').toLowerCase().includes(q) || ('#'+i.number).includes(q)); } - // showClosed controls list view; board view always shows done column - if (!showClosed && currentView === 'list') items = items.filter(i => i.state !== 'closed'); return items; } @@ -348,7 +361,10 @@ function renderSidebar() { if (sec) items = items.filter(i => sec.repos.includes(i._repo) && sec.filter(i)); return items.length; }; - const totalOpen = allIssues.filter(i=>i.state!=='closed').length; + const totalOpen = allIssues.filter(i=>i.state!=='closed' && !isClientProject(i) && !isThought(i)).length; + const howardCount = allIssues.filter(i=>i.state!=='closed' && getAssignee(i)==='howard').length; + const mikeCount = allIssues.filter(i=>i.state!=='closed' && getAssignee(i)==='mike').length; + const thoughtCount = allIssues.filter(i=>i.state!=='closed' && isThought(i)).length; let html = ` + + + + `; for (const sec of SECTIONS.filter(s=>s.id.startsWith('rmm'))) { @@ -398,6 +422,12 @@ function renderSidebar() { `; } + // Thoughts section + html += ``; + html += ``; + sb.innerHTML = html; } @@ -425,9 +455,6 @@ function renderToolbar() { - `; @@ -460,7 +487,7 @@ function makeCard(i) {
${tags.map(l=>`${esc(l.name)}`).join('')}${assignee?`${assignee}`:''}
${p?`${p}`:''} - ${timeAgo(getDiscovered(i)||i.created_at)} old · upd ${timeAgo(i.updated_at)} + ${getDueDate(i)?`due ${getDueDate(i)} · `:''}${timeAgo(getDiscovered(i)||i.created_at)} old
`; } @@ -577,8 +604,14 @@ async function showDetail(repo, num) {
Created ${new Date(issue.created_at).toLocaleString()} · - Updated ${new Date(issue.updated_at).toLocaleString()} · - Open in Gitea + Updated ${new Date(issue.updated_at).toLocaleString()} + ${getDueDate(issue) ? ` · Due: ${getDueDate(issue)}` : ''} + · Open in Gitea +
+
+ + + ${getDueDate(issue) ? '' : ''}