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>
This commit is contained in:
91
TaskTracker.Api/wwwroot/js/pages/context.js
Normal file
91
TaskTracker.Api/wwwroot/js/pages/context.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import * as api from '../api.js';
|
||||
|
||||
const el = () => document.getElementById('page-context');
|
||||
|
||||
export async function initContext() {
|
||||
el().innerHTML = `
|
||||
<h1 class="page-title">Context</h1>
|
||||
<div class="section-title">App Summary (8 hours)</div>
|
||||
<div id="ctx-summary" class="card mb-16"></div>
|
||||
<div class="flex-between mb-8">
|
||||
<div class="section-title" style="margin-bottom:0">Recent Events</div>
|
||||
<select class="form-select" id="ctx-minutes" style="width:auto">
|
||||
<option value="15">Last 15 min</option>
|
||||
<option value="30" selected>Last 30 min</option>
|
||||
<option value="60">Last hour</option>
|
||||
<option value="120">Last 2 hours</option>
|
||||
<option value="480">Last 8 hours</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="ctx-events" class="card table-wrap"></div>`;
|
||||
|
||||
document.getElementById('ctx-minutes').addEventListener('change', loadEvents);
|
||||
await Promise.all([loadSummary(), loadEvents()]);
|
||||
}
|
||||
|
||||
async function loadSummary() {
|
||||
try {
|
||||
const summary = await api.context.summary();
|
||||
const container = document.getElementById('ctx-summary');
|
||||
if (!summary || !summary.length) {
|
||||
container.innerHTML = `<div class="empty-state">No activity recorded</div>`;
|
||||
return;
|
||||
}
|
||||
container.innerHTML = `
|
||||
<table>
|
||||
<thead><tr><th>Application</th><th>Category</th><th>Events</th><th>First Seen</th><th>Last Seen</th></tr></thead>
|
||||
<tbody>
|
||||
${summary.map(s => `
|
||||
<tr>
|
||||
<td>${esc(s.appName)}</td>
|
||||
<td>${esc(s.category)}</td>
|
||||
<td>${s.eventCount}</td>
|
||||
<td>${formatTime(s.firstSeen)}</td>
|
||||
<td>${formatTime(s.lastSeen)}</td>
|
||||
</tr>`).join('')}
|
||||
</tbody>
|
||||
</table>`;
|
||||
} catch (e) {
|
||||
document.getElementById('ctx-summary').innerHTML = `<div class="empty-state">Failed to load summary</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadEvents() {
|
||||
const minutes = parseInt(document.getElementById('ctx-minutes').value);
|
||||
try {
|
||||
const events = await api.context.recent(minutes);
|
||||
const container = document.getElementById('ctx-events');
|
||||
if (!events || !events.length) {
|
||||
container.innerHTML = `<div class="empty-state">No recent events</div>`;
|
||||
return;
|
||||
}
|
||||
container.innerHTML = `
|
||||
<table>
|
||||
<thead><tr><th>Source</th><th>App</th><th>Window Title</th><th>URL</th><th>Time</th></tr></thead>
|
||||
<tbody>
|
||||
${events.map(e => `
|
||||
<tr>
|
||||
<td>${esc(e.source)}</td>
|
||||
<td>${esc(e.appName)}</td>
|
||||
<td class="truncate">${esc(e.windowTitle)}</td>
|
||||
<td class="truncate">${e.url ? esc(e.url) : '-'}</td>
|
||||
<td>${formatTime(e.timestamp)}</td>
|
||||
</tr>`).join('')}
|
||||
</tbody>
|
||||
</table>`;
|
||||
} catch (e) {
|
||||
document.getElementById('ctx-events').innerHTML = `<div class="empty-state">Failed to load events</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function formatTime(iso) {
|
||||
const d = new Date(iso);
|
||||
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||
}
|
||||
|
||||
function esc(str) {
|
||||
if (!str) return '';
|
||||
const d = document.createElement('div');
|
||||
d.textContent = str;
|
||||
return d.innerHTML;
|
||||
}
|
||||
Reference in New Issue
Block a user