Build & Deploy / deploy (push) Successful in 2m41s
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
208 lines
9.1 KiB
Plaintext
208 lines
9.1 KiB
Plaintext
---
|
|
import Base from '../layouts/Base.astro';
|
|
import { profiles, standarder, kategorier } from '../data/profiles';
|
|
|
|
const searchString = (p: (typeof profiles)[number]) =>
|
|
[p.standard, p.kategori ?? '', p.navn, p.id, p.customization, p.dokumenter.join(' '), p.anvendelse, p.bemaerkninger]
|
|
.join(' ')
|
|
.toLowerCase();
|
|
|
|
// Registerprofilerne af "single"-typen står ikke i G26, dem henviser vi til registret for.
|
|
const kunIRegistret = new Set(['Procurement-OrdRes-1.0', 'Procurement-BilSimReminderOnly-1.0']);
|
|
|
|
const kilde = (p: (typeof profiles)[number]) => {
|
|
if (kunIRegistret.has(p.id))
|
|
return { label: 'Nemhandelsregistret', url: 'https://nemhandel.dk/nemhandelsregisteret' };
|
|
if (p.standard.startsWith('Nemhandel'))
|
|
return { label: 'OIOUBL Profiler (G26)', url: 'https://oioubl21.oioubl.dk/documents/en/en/Guidelines/OIOUBL_GUIDE_PROFILES.pdf' };
|
|
if (p.standard.includes('PINT'))
|
|
return p.navn.includes('(kommende)')
|
|
? { label: 'Peppol roadmap', url: 'https://docs.peppol.eu/poac/' }
|
|
: { label: 'docs.peppol.eu \u00b7 EU PINT Billing', url: 'https://docs.peppol.eu/poac/eu/pint-eu/' };
|
|
if (p.navn.includes('Self-Billing'))
|
|
return { label: 'docs.peppol.eu \u00b7 Self-Billing 3.0', url: 'https://docs.peppol.eu/poacc/self-billing/3.0/' };
|
|
if (p.navn.includes('Billing 3.0'))
|
|
return { label: 'docs.peppol.eu \u00b7 BIS Billing 3.0', url: 'https://docs.peppol.eu/poacc/billing/3.0/' };
|
|
return { label: 'docs.peppol.eu \u00b7 BIS v3', url: 'https://docs.peppol.eu/poacc/upgrade-3/' };
|
|
};
|
|
---
|
|
<Base
|
|
title="Profil-opslag (Nemhandel & Peppol)"
|
|
description="Søgbart opslag over Nemhandel OIOUBL- og Peppol BIS/PINT-profiler: profil-id'er, customization-id'er, indeholdte dokumenter og typisk anvendelse."
|
|
>
|
|
<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 rounded-xl border border-border bg-surface/60 py-2.5 pl-9 pr-4 outline-none focus:border-accent"
|
|
/>
|
|
</div>
|
|
<span id="p-count" class="text-sm text-muted">{profiles.length} profiler</span>
|
|
</div>
|
|
|
|
<div class="mt-3 flex flex-wrap gap-2">
|
|
<button type="button" class="p-chip" data-std="" aria-pressed="true">Alle</button>
|
|
{standarder.map((s) => <button type="button" class="p-chip" data-std={s} aria-pressed="false">{s}</button>)}
|
|
</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="p-chip" data-kat="" aria-pressed="true">Alle</button>
|
|
{kategorier.map((k) => <button type="button" class="p-chip" data-kat={k} aria-pressed="false">{k}</button>)}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="mt-6 space-y-3">
|
|
{
|
|
profiles.map((p) => { const k = kilde(p); return (
|
|
<article
|
|
data-profile
|
|
data-standard={p.standard}
|
|
data-kategori={p.kategori ?? ''}
|
|
data-search={searchString(p)}
|
|
class="rounded-2xl border border-border bg-surface/60 p-5 transition-colors hover:border-accent/50"
|
|
>
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<span class="wt-tag wt-tag--accent">
|
|
{p.standard}
|
|
</span>
|
|
{p.kategori && (
|
|
<span
|
|
class="wt-tag wt-muted"
|
|
title="Gruppen profilen står under i Nemhandelsregistrets dropdown"
|
|
>
|
|
Register: {p.kategori}
|
|
</span>
|
|
)}
|
|
</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">
|
|
{p.dokumenter.map((d) => (
|
|
<span class="wt-tag">{d}</span>
|
|
))}
|
|
</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={k.url} target="_blank" rel="noopener" class="text-accent/80 transition-colors hover:text-accent">Kilde: {k.label} ↗</a></p>
|
|
</article>
|
|
); })
|
|
}
|
|
</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>
|
|
|
|
<style is:inline>
|
|
.p-chip {
|
|
border: 1px solid var(--color-border);
|
|
color: var(--color-muted);
|
|
border-radius: 0.6rem;
|
|
padding: 0.35rem 0.8rem;
|
|
font-size: 0.85rem;
|
|
cursor: pointer;
|
|
background: transparent;
|
|
transition: color 0.15s, background-color 0.15s, border-color 0.15s;
|
|
}
|
|
.p-chip:hover { color: var(--color-text); }
|
|
.p-chip[aria-pressed="true"] {
|
|
background: var(--color-surface-2);
|
|
color: var(--color-text);
|
|
border-color: color-mix(in srgb, var(--color-accent) 40%, var(--color-border));
|
|
}
|
|
.copy { transition: color 0.15s; }
|
|
.copy:hover { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; }
|
|
</style>
|
|
|
|
<script is:inline>
|
|
(function () {
|
|
var cards = [].slice.call(document.querySelectorAll('[data-profile]'));
|
|
var search = document.getElementById('p-search');
|
|
var stdChips = [].slice.call(document.querySelectorAll('.p-chip[data-std]'));
|
|
var katChips = [].slice.call(document.querySelectorAll('.p-chip[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>
|
|
</Base>
|