Files
pw/templates/sider/profiler.html
T
Asger Geel WeirsøeandClaude Opus 5.5 e58a11c2b4 feat: flyt sitet fra Astro til Zola
Samme indhold, URL'er og udseende, bygget af en enkelt zola-binær og Tailwind
standalone-CLI uden Node. Kanonisk domæne er asger.weirsoe.dk.

Blogindlæg ligger som page bundles med frontmatter Zola forstår, så de gamle
stempler er ugyldige, indtil scripts/omstempl.py er kørt med PUBLISH_KEY.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 16:47:08 +01:00

157 lines
6.7 KiB
HTML

{% extends "base.html" %}
{% block main %}
{% set d = load_data(path="data/profiles.json") %}
<h1 class="text-4xl font-bold tracking-tight">Profil-opslag</h1>
<p class="mt-4 max-w-2xl text-muted">
Browse og søg i e-dokumentprofilerne for <strong class="text-text">Nemhandel OIOUBL</strong> og
<strong class="text-text">Peppol BIS/PINT</strong>: profil-/proces-id, customization-id, hvilke
dokumenter profilen indeholder og typisk anvendelse. Klik på et id for at kopiere det.
</p>
<p class="mt-3 max-w-2xl text-sm text-muted">
De danske profiler står i samme rækkefølge og under samme gruppenavne som i
<strong class="text-text">Nemhandelsregistrets</strong> profil-dropdown (“Profiler for: …”), så du kan
slå direkte op mellem registrering og dokumentindhold.
</p>
<div class="mt-8 flex flex-col gap-4 sm:flex-row sm:items-center">
<div class="relative flex-1">
<span class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted">⌕</span>
<input
id="p-search"
type="search"
placeholder="Søg profil, id, dokument…"
autocomplete="off"
aria-label="Søg profiler"
class="w-full wt-input wt-input--lg pl-9"
/>
</div>
<span id="p-count" class="text-sm text-muted">{{ d.profiles | length }} profiler</span>
</div>
<div class="mt-3 flex flex-wrap gap-2">
<button type="button" class="wt-btn wt-btn--ghost wt-btn--sm" data-std="" aria-pressed="true">Alle</button>
{% for s in d.standarder %}<button type="button" class="wt-btn wt-btn--ghost wt-btn--sm" data-std="{{ s }}" aria-pressed="false">{{ s }}</button>{% endfor %}
</div>
<div class="mt-3">
<span class="mr-2 text-xs text-muted">Nemhandelsregistrets kategori:</span>
<span class="mt-1 inline-flex flex-wrap gap-2 align-middle">
<button type="button" class="wt-btn wt-btn--ghost wt-btn--sm" data-kat="" aria-pressed="true">Alle</button>
{% for k in d.kategorier %}<button type="button" class="wt-btn wt-btn--ghost wt-btn--sm" data-kat="{{ k }}" aria-pressed="false">{{ k }}</button>{% endfor %}
</span>
</div>
<div class="mt-6 space-y-3">
{%- for p in d.profiles %}
<article
data-profile
data-standard="{{ p.standard }}"
data-kategori="{{ p.kategori }}"
data-search="{{ (p.standard ~ " " ~ p.kategori ~ " " ~ p.navn ~ " " ~ p.id ~ " " ~ p.customization ~ " " ~ (p.dokumenter | join(sep=" ")) ~ " " ~ p.anvendelse ~ " " ~ p.bemaerkninger) | lower }}"
class="wt-card wt-card--hover"
>
<div class="flex flex-wrap items-center gap-2">
<span class="wt-tag wt-tag--accent">
{{ p.standard }}
</span>
{% if p.kategori %}
<span
class="wt-tag wt-muted"
title="Gruppen profilen står under i Nemhandelsregistrets dropdown"
>
Register: {{ p.kategori }}
</span>
{% endif %}
</div>
<h2 class="mt-2 text-lg font-semibold tracking-tight">{{ p.navn }}</h2>
<dl class="mt-3 grid gap-1 text-xs sm:grid-cols-[10rem_1fr]">
<dt class="text-muted">Profil/proces-id</dt>
<dd>
<button type="button" data-copy="{{ p.id }}" title="Klik for at kopiere" class="copy break-all text-left font-mono">
{{ p.id }}
</button>
</dd>
<dt class="text-muted">Customization-id</dt>
<dd>
<button type="button" data-copy="{{ p.customization }}" title="Klik for at kopiere" class="copy break-all text-left font-mono">
{{ p.customization }}
</button>
</dd>
</dl>
<div class="mt-3 flex flex-wrap gap-1.5">
{% for doc in p.dokumenter %}
<span class="wt-tag">{{ doc }}</span>
{% endfor %}
</div>
<p class="mt-3 text-sm text-muted">{{ p.anvendelse }}</p>
<p class="mt-2 text-xs text-muted">
<span class="font-semibold text-muted">Bemærk:</span> {{ p.bemaerkninger }}
</p>
<p class="mt-3 text-xs"><a href="{{ p.kilde.url | safe }}" target="_blank" rel="noopener" class="text-accent/80 transition-colors hover:text-accent">Kilde: {{ p.kilde.label }} ↗</a></p>
</article>
{%- endfor %}
</div>
<p id="p-none" class="mt-6 text-sm text-muted" hidden>Ingen profiler matcher.</p>
<p class="mt-10 text-xs text-muted">
Kilder: OIOUBL Profiler-guiden (G26) v1.3 (dokumentlister, tabel 1-3), Nemhandelsregistret
(gruppering og profilnavne) og Peppol BIS/PINT (docs.peppol.eu). * = ApplicationResponse bruges kun
til afvisning; det forretningsmæssige svar på en ordre er altid OrderResponse/OrderResponseSimple.
Øjebliksbillede.. verificér altid id'er mod den officielle dokumentation før brug.
</p>
<script>
(function () {
var cards = [].slice.call(document.querySelectorAll('[data-profile]'));
var search = document.getElementById('p-search');
var stdChips = [].slice.call(document.querySelectorAll('button[data-std]'));
var katChips = [].slice.call(document.querySelectorAll('button[data-kat]'));
var count = document.getElementById('p-count');
var none = document.getElementById('p-none');
var q = '', std = '', kat = '';
function apply() {
var shown = 0;
cards.forEach(function (c) {
var okS = !std || c.getAttribute('data-standard') === std;
var okK = !kat || c.getAttribute('data-kategori') === kat;
var okQ = !q || c.getAttribute('data-search').indexOf(q) > -1;
var hit = okS && okK && okQ;
c.hidden = !hit;
if (hit) shown++;
});
count.textContent = shown + (shown === 1 ? ' profil' : ' profiler');
none.hidden = shown > 0;
}
search.addEventListener('input', function () { q = search.value.trim().toLowerCase(); apply(); });
function wire(group, attr, set) {
group.forEach(function (b) {
b.addEventListener('click', function () {
set(b.getAttribute(attr) || '');
group.forEach(function (x) { x.setAttribute('aria-pressed', x === b ? 'true' : 'false'); });
apply();
});
});
}
wire(stdChips, 'data-std', function (v) { std = v; });
wire(katChips, 'data-kat', function (v) { kat = v; });
document.addEventListener('click', function (e) {
var btn = e.target.closest && e.target.closest('[data-copy]');
if (!btn || !navigator.clipboard) return;
navigator.clipboard.writeText(btn.getAttribute('data-copy')).then(function () {
var orig = btn.textContent;
btn.textContent = 'kopieret ✓';
setTimeout(function () { btn.textContent = orig; }, 1200);
}).catch(function () {});
});
var qp = new URLSearchParams(location.search).get('q');
if (qp) { search.value = qp; q = qp.trim().toLowerCase(); }
apply();
})();
</script>
{% endblock %}