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>
157 lines
6.7 KiB
HTML
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 %}
|