nemhandel: omdøb CVR-opslag fra Peppol til NemHandel-opslag (ny rute /nemhandel + /api/nemhandel, redirect fra /peppol)
Build & Deploy / deploy (push) Successful in 2m5s
Build & Deploy / deploy (push) Successful in 2m5s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
24492fae93
commit
898c37634b
@@ -36,6 +36,11 @@ import Base from '../layouts/Base.astro';
|
||||
'<a href="mailto:$1" class="text-accent underline">$1</a>');
|
||||
return html;
|
||||
}
|
||||
// Track spørgsmålet i Matomo FRA browseren, så det knyttes til den besøgendes
|
||||
// egen visitor-profil (cookie/visitor-ID, IP, browser) i stedet for en server-IP.
|
||||
function track(action, q) {
|
||||
try { (window._paq = window._paq || []).push(['trackEvent', 'Chat', action, q]); } catch (_) {}
|
||||
}
|
||||
function bubble(role, text) {
|
||||
var log = document.getElementById('chat-log');
|
||||
var d = document.createElement('div');
|
||||
@@ -61,11 +66,13 @@ import Base from '../layouts/Base.astro';
|
||||
body: JSON.stringify({ messages: history.slice(-10) }),
|
||||
});
|
||||
var data = await r.json();
|
||||
if (!r.ok) { wait.textContent = data.error || 'Chatten er offline lige nu — prøv senere.'; return; }
|
||||
if (!r.ok) { wait.textContent = data.error || 'Chatten er offline lige nu — prøv senere.'; track('afvist', q); return; }
|
||||
wait.innerHTML = linkify(data.reply);
|
||||
history.push({ role: 'assistant', content: data.reply });
|
||||
track('besvaret', q);
|
||||
} catch (_) {
|
||||
wait.textContent = 'Kunne ikke nå chatten — prøv igen om lidt.';
|
||||
track('fejl', q);
|
||||
} finally { btn.disabled = false; input.focus(); }
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -80,7 +80,7 @@ const jsonLd = [profilePage, faqPage(faqs)];
|
||||
<h2 class="text-2xl font-bold tracking-tight">Mere end et CV</h2>
|
||||
<div class="mt-5 grid gap-4 sm:grid-cols-2">
|
||||
{[
|
||||
{ href: '/peppol', title: 'Peppol-opslag', desc: 'Slå et CVR op og se om virksomheden er registreret i NemHandel/Peppol — og hvilke profilpakker den understøtter.' },
|
||||
{ href: '/nemhandel', title: 'NemHandel-opslag', desc: 'Slå et CVR-nummer op og se om virksomheden er registreret i NemHandelsregisteret — og hvilke profilpakker den understøtter.' },
|
||||
{ href: '/chat', title: 'Spørg mit CV', desc: 'En lille AI der har læst mit CV og svarer på spørgsmål om mig. Prøv den af.' },
|
||||
{ href: '/status', title: 'Homelab-status', desc: 'Live driftsstatus for de offentlige dele af mit homelab. Self-hosted, selvfølgelig.' },
|
||||
{ href: '/now', title: 'Now', desc: 'Hvad jeg roder med lige nu — plus et aktivitets-heatmap fra min egen Gitea.' },
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
---
|
||||
<Base title="NemHandel-opslag" description="Slå et CVR-nummer op og se om virksomheden er registreret i NemHandelsregisteret — og hvilke profilpakker den understøtter.">
|
||||
<h1 class="text-4xl font-bold tracking-tight">NemHandel-opslag</h1>
|
||||
<p class="mt-4 max-w-2xl text-muted">
|
||||
Tast et CVR-nummer og se om virksomheden er registreret i NemHandelsregisteret, og hvilke
|
||||
<strong class="text-text">profilpakker</strong> den understøtter. Det er et øjebliksbillede,
|
||||
ikke en autoritativ kilde.
|
||||
</p>
|
||||
|
||||
<form id="pep-form" class="mt-8 flex gap-3">
|
||||
<input id="pep-cvr" type="text" inputmode="numeric" pattern="[0-9]{8}" maxlength="8" required
|
||||
placeholder="CVR (8 cifre)" autocomplete="off"
|
||||
class="w-44 rounded-xl border border-border bg-surface/60 px-4 py-2.5 outline-none focus:border-accent" />
|
||||
<button type="submit" class="rounded-xl bg-accent px-5 py-2.5 font-medium text-bg transition-transform hover:-translate-y-0.5">Slå op</button>
|
||||
</form>
|
||||
|
||||
<div id="pep-result" class="mt-8" hidden>
|
||||
<div class="rounded-2xl border border-border bg-surface/60 p-5">
|
||||
<div class="flex flex-wrap items-baseline justify-between gap-2">
|
||||
<h2 id="pep-name" class="text-xl font-semibold tracking-tight"></h2>
|
||||
<span id="pep-badge" class="rounded-full px-3 py-1 text-sm font-medium"></span>
|
||||
</div>
|
||||
<p id="pep-cvr-line" class="mt-1 text-sm text-muted"></p>
|
||||
<div id="pep-caps" class="mt-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
<p id="pep-error" class="mt-8 text-sm text-muted" hidden></p>
|
||||
|
||||
<script is:inline>
|
||||
(function () {
|
||||
var form = document.getElementById('pep-form');
|
||||
if (!form || form.__wired) return; form.__wired = true;
|
||||
var esc = function (s) { return String(s).replace(/[&<>"]/g, function (c) {
|
||||
return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; }); };
|
||||
form.addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
var res = document.getElementById('pep-result'), err = document.getElementById('pep-error');
|
||||
res.hidden = true; err.hidden = true;
|
||||
var cvr = document.getElementById('pep-cvr').value.trim();
|
||||
var btn = form.querySelector('button'); btn.disabled = true; btn.textContent = 'Slår op…';
|
||||
try {
|
||||
var r = await fetch('/api/nemhandel/' + encodeURIComponent(cvr));
|
||||
var data = await r.json();
|
||||
if (!r.ok) { err.textContent = data.error || 'Noget gik galt — prøv igen.'; err.hidden = false; return; }
|
||||
document.getElementById('pep-name').textContent = data.name || 'Ukendt navn';
|
||||
document.getElementById('pep-cvr-line').textContent = 'CVR ' + data.cvr;
|
||||
var badge = document.getElementById('pep-badge');
|
||||
badge.textContent = data.registered ? 'Registreret i NemHandel' : 'Ikke registreret';
|
||||
badge.className = 'rounded-full px-3 py-1 text-sm font-medium ' +
|
||||
(data.registered ? 'bg-accent/15 text-accent' : 'border border-border text-muted');
|
||||
var caps = document.getElementById('pep-caps');
|
||||
if (data.registered && (data.profiles.length || data.networks.length)) {
|
||||
caps.innerHTML =
|
||||
(data.networks.length ? '<p class="text-xs font-semibold uppercase tracking-widest text-accent">Netværk</p>' +
|
||||
'<div class="mt-2 flex flex-wrap gap-2">' + data.networks.map(function (n) {
|
||||
return '<span class="rounded-full border border-border bg-surface-2 px-3 py-1 text-sm">' + esc(n) + '</span>'; }).join('') + '</div>' : '') +
|
||||
(data.profiles.length ? '<p class="mt-4 text-xs font-semibold uppercase tracking-widest text-accent">Profilpakker</p>' +
|
||||
'<div class="mt-2 flex flex-wrap gap-2">' + data.profiles.map(function (p) {
|
||||
return '<span class="rounded-full border border-border bg-surface-2 px-3 py-1 text-sm">' + esc(p) + '</span>'; }).join('') + '</div>' : '');
|
||||
} else {
|
||||
caps.innerHTML = '<p class="text-sm text-muted">' + (data.registered
|
||||
? 'Registreret, men ingen profildata i seneste opdatering.'
|
||||
: 'Virksomheden findes i CVR, men er ikke registreret som e-faktura-modtager.') + '</p>';
|
||||
}
|
||||
res.hidden = false;
|
||||
} catch (_) {
|
||||
err.textContent = 'Kunne ikke nå opslagstjenesten — prøv igen om lidt.'; err.hidden = false;
|
||||
} finally { btn.disabled = false; btn.textContent = 'Slå op'; }
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<p class="mt-10 text-xs text-muted/80">
|
||||
Til nørderne: API’et er frit tilgængeligt — <code class="rounded bg-surface-2 px-1.5 py-0.5">curl https://asger.weirsøe.dk/api/nemhandel/<cvr></code> (rate-limited).
|
||||
</p>
|
||||
</Base>
|
||||
+14
-76
@@ -1,78 +1,16 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
// Gammel rute — omdøbt til /nemhandel. Redirect så eksisterende links ikke 404'er.
|
||||
---
|
||||
<Base title="Peppol-opslag" description="Slå et CVR-nummer op og se om virksomheden er registreret i NemHandel/Peppol — og hvilke profilpakker den understøtter.">
|
||||
<h1 class="text-4xl font-bold tracking-tight">Peppol-opslag</h1>
|
||||
<p class="mt-4 max-w-2xl text-muted">
|
||||
Tast et CVR-nummer og se om virksomheden er registreret i NemHandel/Peppol, og hvilke
|
||||
<strong class="text-text">profilpakker</strong> den understøtter. Data kommer fra mit eget
|
||||
crawl af de offentlige registre.. så regn med et øjebliksbillede, ikke en autoritativ kilde.
|
||||
</p>
|
||||
|
||||
<form id="pep-form" class="mt-8 flex gap-3">
|
||||
<input id="pep-cvr" type="text" inputmode="numeric" pattern="[0-9]{8}" maxlength="8" required
|
||||
placeholder="CVR (8 cifre)" autocomplete="off"
|
||||
class="w-44 rounded-xl border border-border bg-surface/60 px-4 py-2.5 outline-none focus:border-accent" />
|
||||
<button type="submit" class="rounded-xl bg-accent px-5 py-2.5 font-medium text-bg transition-transform hover:-translate-y-0.5">Slå op</button>
|
||||
</form>
|
||||
|
||||
<div id="pep-result" class="mt-8" hidden>
|
||||
<div class="rounded-2xl border border-border bg-surface/60 p-5">
|
||||
<div class="flex flex-wrap items-baseline justify-between gap-2">
|
||||
<h2 id="pep-name" class="text-xl font-semibold tracking-tight"></h2>
|
||||
<span id="pep-badge" class="rounded-full px-3 py-1 text-sm font-medium"></span>
|
||||
</div>
|
||||
<p id="pep-cvr-line" class="mt-1 text-sm text-muted"></p>
|
||||
<div id="pep-caps" class="mt-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
<p id="pep-error" class="mt-8 text-sm text-muted" hidden></p>
|
||||
|
||||
<script is:inline>
|
||||
(function () {
|
||||
var form = document.getElementById('pep-form');
|
||||
if (!form || form.__wired) return; form.__wired = true;
|
||||
var esc = function (s) { return String(s).replace(/[&<>"]/g, function (c) {
|
||||
return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; }); };
|
||||
form.addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
var res = document.getElementById('pep-result'), err = document.getElementById('pep-error');
|
||||
res.hidden = true; err.hidden = true;
|
||||
var cvr = document.getElementById('pep-cvr').value.trim();
|
||||
var btn = form.querySelector('button'); btn.disabled = true; btn.textContent = 'Slår op…';
|
||||
try {
|
||||
var r = await fetch('/api/peppol/' + encodeURIComponent(cvr));
|
||||
var data = await r.json();
|
||||
if (!r.ok) { err.textContent = data.error || 'Noget gik galt — prøv igen.'; err.hidden = false; return; }
|
||||
document.getElementById('pep-name').textContent = data.name || 'Ukendt navn';
|
||||
document.getElementById('pep-cvr-line').textContent = 'CVR ' + data.cvr;
|
||||
var badge = document.getElementById('pep-badge');
|
||||
badge.textContent = data.registered ? 'Registreret i NemHandel/Peppol' : 'Ikke registreret';
|
||||
badge.className = 'rounded-full px-3 py-1 text-sm font-medium ' +
|
||||
(data.registered ? 'bg-accent/15 text-accent' : 'border border-border text-muted');
|
||||
var caps = document.getElementById('pep-caps');
|
||||
if (data.registered && (data.profiles.length || data.networks.length)) {
|
||||
caps.innerHTML =
|
||||
(data.networks.length ? '<p class="text-xs font-semibold uppercase tracking-widest text-accent">Netværk</p>' +
|
||||
'<div class="mt-2 flex flex-wrap gap-2">' + data.networks.map(function (n) {
|
||||
return '<span class="rounded-full border border-border bg-surface-2 px-3 py-1 text-sm">' + esc(n) + '</span>'; }).join('') + '</div>' : '') +
|
||||
(data.profiles.length ? '<p class="mt-4 text-xs font-semibold uppercase tracking-widest text-accent">Profilpakker</p>' +
|
||||
'<div class="mt-2 flex flex-wrap gap-2">' + data.profiles.map(function (p) {
|
||||
return '<span class="rounded-full border border-border bg-surface-2 px-3 py-1 text-sm">' + esc(p) + '</span>'; }).join('') + '</div>' : '');
|
||||
} else {
|
||||
caps.innerHTML = '<p class="text-sm text-muted">' + (data.registered
|
||||
? 'Registreret, men ingen profildata i mit seneste crawl.'
|
||||
: 'Virksomheden findes i CVR, men er ikke registreret som e-faktura-modtager.') + '</p>';
|
||||
}
|
||||
res.hidden = false;
|
||||
} catch (_) {
|
||||
err.textContent = 'Kunne ikke nå opslagstjenesten — prøv igen om lidt.'; err.hidden = false;
|
||||
} finally { btn.disabled = false; btn.textContent = 'Slå op'; }
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<p class="mt-10 text-xs text-muted/80">
|
||||
Til nørderne: API’et er frit tilgængeligt — <code class="rounded bg-surface-2 px-1.5 py-0.5">curl https://asger.weirsøe.dk/api/peppol/<cvr></code> (rate-limited).
|
||||
</p>
|
||||
</Base>
|
||||
<!doctype html>
|
||||
<html lang="da">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="refresh" content="0; url=/nemhandel" />
|
||||
<link rel="canonical" href="https://asger.xn--weirse-fya.dk/nemhandel" />
|
||||
<title>NemHandel-opslag</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Siden er flyttet til <a href="/nemhandel">/nemhandel</a>…</p>
|
||||
<script>location.replace('/nemhandel');</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,7 +9,7 @@ export async function GET() {
|
||||
{ type: 'page', title: 'Projekter', url: '/projekter' },
|
||||
{ type: 'page', title: 'Blog', url: '/blog' },
|
||||
{ type: 'page', title: 'Tags', url: '/tags' },
|
||||
{ type: 'page', title: 'Peppol-opslag', url: '/peppol' },
|
||||
{ type: 'page', title: 'NemHandel-opslag', url: '/nemhandel' },
|
||||
{ type: 'page', title: 'Spørg mit CV (AI-chat)', url: '/chat' },
|
||||
{ type: 'page', title: 'Status (homelab)', url: '/status' },
|
||||
{ type: 'page', title: 'Now', url: '/now' },
|
||||
|
||||
Reference in New Issue
Block a user