side: /now m. git-aktivitets-heatmap fra egen Gitea (build-time)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Asger Geel Weirsøe
2026-06-11 21:59:51 +02:00
co-authored by Claude Opus 4.8
parent 02dacf32da
commit c21974a3ce
+82
View File
@@ -0,0 +1,82 @@
---
import Base from '../layouts/Base.astro';
import { SITE } from '../consts';
// Hvad jeg roder med lige nu — opdateres manuelt, det er hele pointen med en /now-side.
const focus = [
'Elektronisk fakturering hos Digisense: Peppol, NemHandel og løsningsarkitektur',
'HD 1. del i Erhvervsøkonomi på AAU (eksamener til sommer)',
'Homelab: crawlkit, denne side, og alt for mange LXC-containere',
];
// Build-time: aktivitets-heatmap fra min Gitea (offentligt API). Fejler stille —
// siden bygger fint uden graf hvis Gitea er nede under build.
type Beat = { timestamp: number; contributions: number };
let perDay = new Map<string, number>();
try {
const r = await fetch(`${SITE.giteaBase}/api/v1/users/${SITE.giteaUser}/heatmap`);
if (r.ok) for (const b of (await r.json()) as Beat[]) {
const d = new Date(b.timestamp * 1000).toISOString().slice(0, 10);
perDay.set(d, (perDay.get(d) || 0) + b.contributions);
}
} catch { /* ingen graf, ingen drama */ }
// 26 uger bagud, uger som kolonner (man-søn), niveau 0-4
const WEEKS = 26;
const today = new Date(); today.setHours(12, 0, 0, 0);
const start = new Date(today); start.setDate(start.getDate() - (WEEKS * 7 - 1) - ((today.getDay() + 6) % 7));
const weeks: { date: string; n: number }[][] = [];
const cur = new Date(start);
while (cur <= today) {
const week: { date: string; n: number }[] = [];
for (let i = 0; i < 7 && cur <= today; i++) {
const key = cur.toISOString().slice(0, 10);
week.push({ date: key, n: perDay.get(key) || 0 });
cur.setDate(cur.getDate() + 1);
}
weeks.push(week);
}
const level = (n: number) => (n === 0 ? 0 : n < 3 ? 1 : n < 6 ? 2 : n < 12 ? 3 : 4);
const total = [...perDay.values()].reduce((a, b) => a + b, 0);
const updated = today.toLocaleDateString('da-DK', { year: 'numeric', month: 'long', day: 'numeric' });
---
<Base title="Now" description="Hvad jeg arbejder på lige nu — plus et aktivitets-heatmap fra min self-hostede Gitea.">
<h1 class="text-4xl font-bold tracking-tight">Now</h1>
<p class="mt-2 text-sm text-muted">Senest opdateret {updated} · à la <a class="text-accent hover:underline" href="https://nownownow.com/about" target="_blank" rel="noopener">nownownow.com</a></p>
<section class="mt-8">
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Lige nu</h2>
<ul class="mt-4 space-y-3">
{focus.map((f) => (
<li class="rounded-2xl border border-border bg-surface/60 p-4 text-sm">{f}</li>
))}
</ul>
</section>
{weeks.length > 0 && (
<section class="mt-10">
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Git-aktivitet</h2>
<p class="mt-2 text-sm text-muted">{total} bidrag i mit eget Gitea det seneste år — de seneste {WEEKS} uger:</p>
<div class="mt-4 overflow-x-auto rounded-2xl border border-border bg-surface/60 p-4">
<div class="heatmap" role="img" aria-label="Aktivitets-heatmap over git-bidrag">
{weeks.map((w) => (
<div class="hm-col">
{w.map((d) => <span class={`hm-cell hm-${level(d.n)}`} title={`${d.date}: ${d.n} bidrag`} />)}
</div>
))}
</div>
</div>
<p class="mt-2 text-xs text-muted/80">Offentlige projekter ligger på <a class="text-accent hover:underline" href={`${SITE.giteaBase}/${SITE.giteaUser}`} target="_blank" rel="noopener">gitea.weircon.dk</a>.</p>
</section>
)}
<style>
.heatmap { display: flex; gap: 3px; width: max-content; }
.hm-col { display: flex; flex-direction: column; gap: 3px; }
.hm-cell { width: 11px; height: 11px; border-radius: 2.5px; background: var(--color-surface-2); }
.hm-1 { background: color-mix(in srgb, var(--color-accent) 30%, var(--color-surface-2)); }
.hm-2 { background: color-mix(in srgb, var(--color-accent) 55%, var(--color-surface-2)); }
.hm-3 { background: color-mix(in srgb, var(--color-accent) 80%, var(--color-surface-2)); }
.hm-4 { background: var(--color-accent); }
</style>
</Base>