feat(render): card owner pill + short ISO date span

closes board-viewer-card-meta-fix

Review found two acceptance fields missing from cards:
- project_owner was in JSON but never displayed
- last_commit_iso was only consumed for relative age; no absolute date

Now both rendered (server-side src/render.ts + client-side static/board.js
in sync), with 4 new TDD tests:
- renders owner pill when project_owner is set
- omits owner pill when empty
- renders short YYYY-MM-DD date when last_commit_iso is set
- omits date span when null

CSS: .badge.owner = transparent + border (visually distinct from solid
.badge.project), .date = small muted tabular-nums. Smoke-verified in browser.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
vitya
2026-05-22 15:42:59 +03:00
parent 7cebeda219
commit 930e404545
6 changed files with 79 additions and 7 deletions

View File

@@ -148,6 +148,42 @@ describe('renderBoard', () => {
expect(html).toContain('"embed-test"');
});
test('renders owner as a pill when project_owner is non-empty', () => {
const html = renderBoard(
[rec({ slug: 'x', project_owner: 'OpeItcLoc03' })],
{ generatedAt: now },
);
expect(html).toMatch(/<span class="badge owner">OpeItcLoc03<\/span>/);
});
test('omits owner pill when project_owner is empty string', () => {
const html = renderBoard(
[rec({ slug: 'x', project_owner: '' })],
{ generatedAt: now },
);
expect(html).not.toContain('badge owner');
});
test('renders last_commit_iso as a short YYYY-MM-DD date span', () => {
const html = renderBoard(
[rec({ slug: 'x', last_commit_iso: '2026-05-19T08:30:00Z' })],
{ generatedAt: now },
);
expect(html).toMatch(/<span class="date"[^>]*>2026-05-19<\/span>/);
});
test('omits date span when last_commit_iso is null', () => {
const html = renderBoard(
[rec({ slug: 'x', last_commit_iso: null })],
{ generatedAt: now },
);
expect(html).not.toMatch(/<span class="date"/);
});
test('writes generation timestamp as data-generated attribute on the header', () => {
const html = renderBoard([], { generatedAt: now });