Files
TaskTracker/TaskTracker.Api/wwwroot/js/pages/dashboard.js
AJ Isaacs e12f78c479 chore: initial commit of TaskTracker project
Existing ASP.NET API with vanilla JS SPA, WindowWatcher, Chrome extension, and MCP server.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:08:45 -05:00

128 lines
4.8 KiB
JavaScript

import * as api from '../api.js';
const el = () => document.getElementById('page-dashboard');
export function initDashboard() {
el().innerHTML = `
<h1 class="page-title">Dashboard</h1>
<div id="dash-active-task"></div>
<div class="section-title mt-16">Task Summary</div>
<div id="dash-stats" class="stats-grid"></div>
<div class="section-title mt-16">Recent Activity (8 hours)</div>
<div id="dash-context" class="card"></div>`;
}
export async function refreshDashboard() {
try {
const [active, allTasks, summary] = await Promise.all([
api.tasks.active(),
api.tasks.list(null, { includeSubTasks: true }),
api.context.summary(),
]);
await renderActiveTask(active);
renderStats(allTasks);
renderContextSummary(summary);
} catch (e) {
console.error('Dashboard refresh failed:', e);
}
}
async function buildParentTrail(task) {
const trail = [];
let current = task;
while (current.parentTaskId) {
try {
current = await api.tasks.get(current.parentTaskId);
trail.unshift({ id: current.id, title: current.title });
} catch {
break;
}
}
return trail;
}
async function renderActiveTask(task) {
const container = document.getElementById('dash-active-task');
if (!task) {
container.innerHTML = `<div class="card"><div class="no-active-task">No active task</div></div>`;
return;
}
const parentTrail = await buildParentTrail(task);
const breadcrumbHtml = parentTrail.length > 0
? `<div class="breadcrumb text-sm mt-8">${parentTrail.map(p => `<span class="breadcrumb-parent">${esc(p.title)}</span><span class="breadcrumb-sep">/</span>`).join('')}<span class="breadcrumb-current">${esc(task.title)}</span></div>`
: '';
const elapsed = task.startedAt ? formatElapsed(new Date(task.startedAt)) : '';
container.innerHTML = `
<div class="card active-task-card">
<div class="card-header">
<div>
<div class="card-title">${esc(task.title)}</div>
${breadcrumbHtml}
${task.description ? `<div class="text-sm text-muted mt-8">${esc(task.description)}</div>` : ''}
${task.category ? `<div class="text-sm text-muted">Category: ${esc(task.category)}</div>` : ''}
${elapsed ? `<div class="text-sm text-muted">Active for ${elapsed}</div>` : ''}
</div>
<div class="btn-group">
<button class="btn btn-warning btn-sm" data-action="pause" data-id="${task.id}">Pause</button>
<button class="btn btn-success btn-sm" data-action="complete" data-id="${task.id}">Complete</button>
</div>
</div>
</div>`;
container.querySelectorAll('[data-action]').forEach(btn => {
btn.addEventListener('click', async () => {
const action = btn.dataset.action;
const id = btn.dataset.id;
try {
if (action === 'pause') await api.tasks.pause(id);
else if (action === 'complete') await api.tasks.complete(id);
refreshDashboard();
} catch (e) { alert(e.message); }
});
});
}
function renderStats(allTasks) {
const counts = { Pending: 0, Active: 0, Paused: 0, Completed: 0, Abandoned: 0 };
allTasks.forEach(t => counts[t.status] = (counts[t.status] || 0) + 1);
const container = document.getElementById('dash-stats');
container.innerHTML = Object.entries(counts).map(([status, count]) => `
<div class="stat-card">
<div class="stat-value">${count}</div>
<div class="stat-label">${status}</div>
</div>`).join('');
}
function renderContextSummary(summary) {
const container = document.getElementById('dash-context');
if (!summary || summary.length === 0) {
container.innerHTML = `<div class="empty-state">No recent activity</div>`;
return;
}
container.innerHTML = summary.slice(0, 10).map(item => `
<div class="summary-item">
<div>
<div class="summary-app">${esc(item.appName)}</div>
<div class="summary-category">${esc(item.category)}</div>
</div>
<div class="summary-count">${item.eventCount}</div>
</div>`).join('');
}
function formatElapsed(since) {
const diff = Math.floor((Date.now() - since.getTime()) / 1000);
if (diff < 60) return `${diff}s`;
if (diff < 3600) return `${Math.floor(diff / 60)}m`;
const h = Math.floor(diff / 3600);
const m = Math.floor((diff % 3600) / 60);
return `${h}h ${m}m`;
}
function esc(str) {
if (!str) return '';
const d = document.createElement('div');
d.textContent = str;
return d.innerHTML;
}