agenthub/src/server/archive.ts
chahinebrini 0884032db1 feat(board): session batch — delete + unified fixed header + in-card live console + reviewer field + 3/4 kanban token-insights redesign
Delete endpoint + trash UI, shared header across all pages, task-log SSE + in-card console, reviewer separate from assignee (board+team), codex board redesign (3/4 kanban + company half-donuts + per-agent bars + square cards).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 20:43:42 +02:00

80 lines
3.0 KiB
TypeScript

import { loadConfig } from '../core/config.js';
import { listTasks } from '../core/services/taskService.js';
import { agentAvatar, designTokensCss, escapeHtml, appHeader, appHeaderCss, taskModalHtml, appHeaderJs } from './ui-shared.js';
import type { IndexEntry } from '../core/index.js';
function compactDuration(ms: number): string {
const s = Math.max(0, Math.floor(ms / 1000));
if (s < 60) return `${s}s`;
const m = Math.floor(s / 60);
if (m < 60) return `${m}m`;
const h = Math.floor(m / 60);
if (h < 48) return `${h}h`;
return `${Math.floor(h / 24)}d`;
}
function totalTime(t: IndexEntry): string {
const start = Date.parse(t.createdAt);
const end = Date.parse(t.updatedAt);
if (Number.isNaN(start) || Number.isNaN(end)) return '';
return compactDuration(end - start);
}
export function renderArchiveHtml(cwd: string): string {
const config = loadConfig(cwd);
const done = listTasks(cwd)
.filter((t) => t.status === 'done')
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
const rows = done.length
? done
.map(
(t) => `<a class="task-row" href="/tasks/${encodeURIComponent(t.id)}">
<span class="id">${escapeHtml(t.id)}</span>
<span class="title">${escapeHtml(t.title)}</span>
<span class="meta">${escapeHtml(totalTime(t))}</span>
<span class="agent">${agentAvatar(t.assignedTo, { size: 24 })}</span>
</a>`,
)
.join('')
: '<div class="empty">No done tasks yet.</div>';
return `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="dark" />
<title>AgentHub Done Archive</title>
<style>
${designTokensCss()}
${appHeaderCss()}
body { padding: 96px 20px 32px; }
.id,.meta { color:var(--muted);font:12px/1.4 var(--font-mono); }
main { max-width:1040px;margin:0 auto; }
.head { display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:10px; }
h1 { font-size:18px;margin:0; }
.task-list { display:grid;gap:7px; }
.task-row { display:grid;grid-template-columns:88px minmax(0,1fr) 90px auto;gap:10px;align-items:center;color:inherit;text-decoration:none;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:10px 12px; }
.task-row:hover { border-color:rgba(88,166,255,.5); }
.title { min-width:0;overflow-wrap:anywhere;font-weight:600; }
.agent { display:flex;justify-content:flex-end; }
.empty { color:var(--muted);background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px; }
@media (max-width:640px){ .task-row{grid-template-columns:1fr;gap:4px}.agent{justify-content:flex-start} }
</style>
</head>
<body>
${appHeader(config.projectName, 'archive')}
<main>
<div class="head">
<h1>Done Archive</h1>
<span class="meta">${done.length} done tasks</span>
</div>
<div class="task-list">${rows}</div>
</main>
${taskModalHtml()}
${appHeaderJs()}
</body>
</html>`;
}