Files
pw/src/pages/nemhandel.astro
T
Asger Geel Weirsøe a352b32fac
Build & Deploy / deploy (push) Successful in 2m41s
Use the theme's chip and tag components for every pill on the site
Links are .wt-chip, labels are .wt-tag, highlighted labels .wt-tag--accent.
Bumps Weircon-wind for the status tag variants.

Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
2026-10-29 18:41:00 +01:00

117 lines
7.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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>
<div id="peppol-net" class="mt-4" hidden></div>
<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 { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); };
var PROF = {
'urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0': 'Peppol BIS Billing 3.0',
'urn:fdc:peppol.eu:poacc:trns:order:3': 'Ordering (Order)',
'urn:fdc:peppol.eu:poacc:trns:order_response:3': 'Order Response',
'urn:fdc:peppol.eu:poacc:trns:despatch_advice:3': 'Despatch Advice',
'urn:fdc:peppol.eu:poacc:trns:catalogue:3': 'Catalogue',
'urn:fdc:peppol.eu:poacc:trns:invoice_response:3': 'Invoice Response',
'urn:fdc:peppol.eu:poacc:trns:mlr:3': 'Message Level Response',
};
async function loadPeppol(cvr) {
var box = document.getElementById('peppol-net'); box.hidden = false;
box.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5"><p class="text-sm text-muted">Slår op i Peppol-netværket…</p></div>';
try {
var r = await fetch('/api/peppol/smp/' + encodeURIComponent(cvr));
var d = await r.json();
if (!r.ok) { box.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5"><p class="text-sm text-muted">Peppol-opslag ikke tilgængeligt lige nu.</p></div>'; return; }
if (!d.on_peppol) {
box.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5"><div class="flex items-baseline justify-between gap-2"><h2 class="text-xl font-semibold tracking-tight">Peppol-netværk</h2><span class="wt-tag wt-muted">Ikke fundet</span></div><p class="mt-2 text-sm text-muted">Virksomheden blev ikke fundet i Peppol Directory via CVR-opslag (kan stadig være på Peppol under et andet identifikator-scheme).</p></div>';
return;
}
var docs = (d.doctypes || []).map(function (dt) {
var label = PROF[dt.customization] || dt.customization;
return '<span class="wt-tag">' + esc(dt.document) + ' · ' + esc(label) + '</span>';
}).join('');
box.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5">' +
'<div class="flex flex-wrap items-baseline justify-between gap-2"><h2 class="text-xl font-semibold tracking-tight">Peppol-netværk</h2>' +
'<span class="rounded-full bg-accent/15 px-3 py-1 text-sm font-medium text-accent">På Peppol</span></div>' +
'<p class="mt-1 text-sm text-muted">' + esc(d.name || '') + (d.country ? ' · ' + esc(d.country) : '') +
' · <span class="font-mono text-xs">' + esc(d.participant_id || '') + '</span>' + (d.reg_date ? ' · reg. ' + esc(d.reg_date) : '') + '</p>' +
'<p class="mt-4 text-xs font-semibold uppercase tracking-widest text-accent">Kan modtage</p>' +
'<div class="mt-2 flex flex-wrap gap-2">' + (docs || '<span class="text-sm text-muted">ingen dokumenttyper</span>') + '</div>' +
'<p class="mt-3 text-xs text-muted/80">Live fra Peppol Directory via egen proxy, intet gemt.</p></div>';
} catch (_) {
box.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5"><p class="text-sm text-muted">Kunne ikke nå Peppol-opslaget.</p></div>';
}
}
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…';
loadPeppol(cvr);
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="wt-tag">' + 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="wt-tag">' + 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/&lt;cvr&gt;</code> (rate-limited).
</p>
</Base>