@edoxen/browser 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/cli.js +4475 -0
- package/package.json +82 -0
- package/src/astro/components/ActionTypeBadge.astro +28 -0
- package/src/astro/components/AgendaTable.astro +49 -0
- package/src/astro/components/BodyBadge.astro +28 -0
- package/src/astro/components/CommitteeCard.astro +58 -0
- package/src/astro/components/CountryFlag.astro +34 -0
- package/src/astro/components/DecadeTimeline.astro +52 -0
- package/src/astro/components/DecisionList.astro +66 -0
- package/src/astro/components/EmptyState.astro +19 -0
- package/src/astro/components/LocaleTabs.astro +44 -0
- package/src/astro/components/MeetingList.astro +63 -0
- package/src/astro/components/MinutesSection.astro +59 -0
- package/src/astro/components/OfficerList.astro +49 -0
- package/src/astro/components/PageHero.astro +42 -0
- package/src/astro/components/PrevNextNav.astro +62 -0
- package/src/astro/components/ReferenceDocuments.astro +38 -0
- package/src/astro/components/UrnBar.astro +49 -0
- package/src/astro/layouts/BaseLayout.astro +79 -0
- package/src/astro/layouts/DecisionLayout.astro +21 -0
- package/src/astro/layouts/MeetingLayout.astro +21 -0
- package/src/astro/pages/404.astro +10 -0
- package/src/astro/pages/[lang]/about.astro +65 -0
- package/src/astro/pages/[lang]/decisions/[urn].astro +126 -0
- package/src/astro/pages/[lang]/decisions/index.astro +27 -0
- package/src/astro/pages/[lang]/index.astro +50 -0
- package/src/astro/pages/[lang]/meetings/[urn].astro +114 -0
- package/src/astro/pages/[lang]/meetings/index.astro +27 -0
- package/src/astro/pages/about.astro +65 -0
- package/src/astro/pages/decisions/[urn].astro +122 -0
- package/src/astro/pages/decisions/index.astro +67 -0
- package/src/astro/pages/index.astro +50 -0
- package/src/astro/pages/meetings/[urn].astro +110 -0
- package/src/astro/pages/meetings/index.astro +20 -0
- package/src/cli/index.ts +195 -0
- package/src/config/index.ts +33 -0
- package/src/config/paths.spec.ts +59 -0
- package/src/config/paths.ts +26 -0
- package/src/config/schema.spec.ts +263 -0
- package/src/config/schema.ts +132 -0
- package/src/config/tokens.spec.ts +57 -0
- package/src/config/tokens.ts +48 -0
- package/src/data/index.ts +36 -0
- package/src/data/lint.spec.ts +79 -0
- package/src/data/lint.ts +77 -0
- package/src/data/load.spec.ts +58 -0
- package/src/data/load.ts +50 -0
- package/src/data/prepare.spec.ts +137 -0
- package/src/data/prepare.ts +199 -0
- package/src/data/project.ts +11 -0
- package/src/data/validate.ts +38 -0
- package/src/errors.ts +25 -0
- package/src/i18n/index.spec.ts +121 -0
- package/src/i18n/index.ts +90 -0
- package/src/index.ts +74 -0
- package/src/integration.ts +233 -0
- package/src/islands/decade-scroller.ts +18 -0
- package/src/islands/print-button.ts +12 -0
- package/src/islands/search-filter-core.spec.ts +84 -0
- package/src/islands/search-filter-core.ts +89 -0
- package/src/islands/search-filter.ts +166 -0
- package/src/islands/section-tabs.ts +35 -0
- package/src/islands/theme-toggle.ts +33 -0
- package/src/islands/urn-copy.ts +29 -0
- package/src/seo/index.spec.ts +76 -0
- package/src/seo/index.ts +85 -0
- package/src/virtual.d.ts +11 -0
- package/styles/base.css +117 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
prev?: { urn: string; title: string }
|
|
4
|
+
next?: { urn: string; title: string }
|
|
5
|
+
basePath: string
|
|
6
|
+
}
|
|
7
|
+
const { prev, next, basePath } = Astro.props
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
{(prev || next) && (
|
|
11
|
+
<nav class="edoxen-prev-next" aria-label="Adjacent items">
|
|
12
|
+
<div class="edoxen-prev-next__cell">
|
|
13
|
+
{prev && (
|
|
14
|
+
<a href={`${basePath}/${encodeURIComponent(prev.urn)}`} rel="prev">
|
|
15
|
+
<span class="edoxen-prev-next__label">← Previous</span>
|
|
16
|
+
<span class="edoxen-prev-next__title">{prev.title}</span>
|
|
17
|
+
</a>
|
|
18
|
+
)}
|
|
19
|
+
</div>
|
|
20
|
+
<div class="edoxen-prev-next__cell edoxen-prev-next__cell--right">
|
|
21
|
+
{next && (
|
|
22
|
+
<a href={`${basePath}/${encodeURIComponent(next.urn)}`} rel="next">
|
|
23
|
+
<span class="edoxen-prev-next__label">Next →</span>
|
|
24
|
+
<span class="edoxen-prev-next__title">{next.title}</span>
|
|
25
|
+
</a>
|
|
26
|
+
)}
|
|
27
|
+
</div>
|
|
28
|
+
</nav>
|
|
29
|
+
)}
|
|
30
|
+
|
|
31
|
+
<style>
|
|
32
|
+
.edoxen-prev-next {
|
|
33
|
+
display: grid;
|
|
34
|
+
grid-template-columns: 1fr 1fr;
|
|
35
|
+
gap: 1rem;
|
|
36
|
+
margin-top: 2rem;
|
|
37
|
+
padding-top: 1rem;
|
|
38
|
+
border-top: 1px solid var(--edoxen-color-border);
|
|
39
|
+
}
|
|
40
|
+
.edoxen-prev-next__cell--right {
|
|
41
|
+
text-align: right;
|
|
42
|
+
}
|
|
43
|
+
.edoxen-prev-next__cell a {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
text-decoration: none;
|
|
47
|
+
padding: 0.5rem 0.75rem;
|
|
48
|
+
border: 1px solid var(--edoxen-color-border);
|
|
49
|
+
border-radius: var(--edoxen-radius-sm);
|
|
50
|
+
background: var(--edoxen-color-surface);
|
|
51
|
+
}
|
|
52
|
+
.edoxen-prev-next__label {
|
|
53
|
+
font-size: 0.75rem;
|
|
54
|
+
color: var(--edoxen-color-muted);
|
|
55
|
+
text-transform: uppercase;
|
|
56
|
+
letter-spacing: 0.05em;
|
|
57
|
+
}
|
|
58
|
+
.edoxen-prev-next__title {
|
|
59
|
+
font-weight: 500;
|
|
60
|
+
color: var(--edoxen-color-text);
|
|
61
|
+
}
|
|
62
|
+
</style>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { SourceUrl, Url } from '@edoxen/edoxen'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
sources?: readonly SourceUrl[]
|
|
6
|
+
urls?: readonly Url[]
|
|
7
|
+
}
|
|
8
|
+
const { sources, urls } = Astro.props
|
|
9
|
+
const items: { href: string; label: string; kind?: string }[] = []
|
|
10
|
+
for (const s of sources ?? []) {
|
|
11
|
+
items.push({ href: s.ref, label: s.format ?? 'document', kind: s.kind })
|
|
12
|
+
}
|
|
13
|
+
for (const u of urls ?? []) {
|
|
14
|
+
items.push({ href: u.ref, label: u.kind ?? 'document', kind: u.kind })
|
|
15
|
+
}
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
{items.length > 0 && (
|
|
19
|
+
<ul class="edoxen-reference-docs">
|
|
20
|
+
{items.map((item) => (
|
|
21
|
+
<li>
|
|
22
|
+
<a href={item.href} rel="noopener">
|
|
23
|
+
{item.label}{item.kind && item.kind !== 'document' ? ` (${item.kind})` : ''}
|
|
24
|
+
</a>
|
|
25
|
+
</li>
|
|
26
|
+
))}
|
|
27
|
+
</ul>
|
|
28
|
+
)}
|
|
29
|
+
|
|
30
|
+
<style>
|
|
31
|
+
.edoxen-reference-docs {
|
|
32
|
+
list-style: square;
|
|
33
|
+
padding-left: 1.5rem;
|
|
34
|
+
}
|
|
35
|
+
.edoxen-reference-docs li {
|
|
36
|
+
margin: 0.25rem 0;
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
urn: string
|
|
4
|
+
label?: string
|
|
5
|
+
copyable?: boolean
|
|
6
|
+
}
|
|
7
|
+
const { urn, label = 'URN', copyable = true } = Astro.props
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<div class="edoxen-urn-bar">
|
|
11
|
+
<span class="edoxen-urn-bar__label">{label}</span>
|
|
12
|
+
<code class="edoxen-urn-bar__value">{urn}</code>
|
|
13
|
+
{copyable && <urn-copy data-text={urn} data-label={label}></urn-copy>}
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<style>
|
|
17
|
+
.edoxen-urn-bar {
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
gap: 0.5rem;
|
|
21
|
+
padding: 0.4rem 0.75rem;
|
|
22
|
+
border: 1px solid var(--edoxen-color-border);
|
|
23
|
+
border-radius: var(--edoxen-radius-sm);
|
|
24
|
+
background: var(--edoxen-color-surface);
|
|
25
|
+
font-size: 0.85rem;
|
|
26
|
+
}
|
|
27
|
+
.edoxen-urn-bar__label {
|
|
28
|
+
color: var(--edoxen-color-muted);
|
|
29
|
+
text-transform: uppercase;
|
|
30
|
+
font-size: 0.7rem;
|
|
31
|
+
letter-spacing: 0.05em;
|
|
32
|
+
}
|
|
33
|
+
.edoxen-urn-bar__value {
|
|
34
|
+
font-family: var(--edoxen-font-mono, ui-monospace, monospace);
|
|
35
|
+
}
|
|
36
|
+
.edoxen-urn-bar :global(.edoxen-urn-copy__button) {
|
|
37
|
+
border: 1px solid var(--edoxen-color-border);
|
|
38
|
+
background: var(--edoxen-color-background);
|
|
39
|
+
color: var(--edoxen-color-text);
|
|
40
|
+
padding: 0.15rem 0.5rem;
|
|
41
|
+
border-radius: var(--edoxen-radius-sm);
|
|
42
|
+
font-size: 0.75rem;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
}
|
|
45
|
+
.edoxen-urn-bar :global(.edoxen-urn-copy__button:hover) {
|
|
46
|
+
background: var(--edoxen-color-accent);
|
|
47
|
+
color: #fff;
|
|
48
|
+
}
|
|
49
|
+
</style>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { EdoxenConfig } from '../../config/index.js'
|
|
3
|
+
import { generateCssTokens } from '../../config/index.js'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
config: EdoxenConfig
|
|
7
|
+
title?: string
|
|
8
|
+
description?: string
|
|
9
|
+
lang?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { config, title, description, lang = config.site.locale } = Astro.props
|
|
13
|
+
const pageTitle = title ?? config.site.title
|
|
14
|
+
const pageDescription = description ?? config.site.description
|
|
15
|
+
const tokens = generateCssTokens(config.theme)
|
|
16
|
+
const canonical = new URL(Astro.url.pathname, config.site.url).toString()
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<!doctype html>
|
|
20
|
+
<html lang={lang} data-theme={config.features.darkMode ? 'auto' : 'light'}>
|
|
21
|
+
<head>
|
|
22
|
+
<meta charset="utf-8" />
|
|
23
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
24
|
+
<title>{pageTitle}</title>
|
|
25
|
+
<meta name="description" content={pageDescription} />
|
|
26
|
+
<link rel="canonical" href={canonical} />
|
|
27
|
+
{config.theme.logos.favicon && (
|
|
28
|
+
<link rel="icon" href={config.theme.logos.favicon} />
|
|
29
|
+
)}
|
|
30
|
+
<meta property="og:title" content={pageTitle} />
|
|
31
|
+
<meta property="og:description" content={pageDescription} />
|
|
32
|
+
<meta property="og:url" content={canonical} />
|
|
33
|
+
<meta name="twitter:card" content="summary" />
|
|
34
|
+
<meta name="twitter:title" content={pageTitle} />
|
|
35
|
+
<meta name="twitter:description" content={pageDescription} />
|
|
36
|
+
<style is:global set:html={tokens} />
|
|
37
|
+
<slot name="head" />
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<header class="edoxen-header">
|
|
41
|
+
<a href="/" class="edoxen-header__brand">
|
|
42
|
+
{config.theme.logos.primary && (
|
|
43
|
+
<img src={config.theme.logos.primary} alt={config.site.title} />
|
|
44
|
+
)}
|
|
45
|
+
<span>{config.site.title}</span>
|
|
46
|
+
</a>
|
|
47
|
+
<nav class="edoxen-header__nav" aria-label="Primary">
|
|
48
|
+
{config.nav.map((item) => (
|
|
49
|
+
!item.locale || item.locale === lang
|
|
50
|
+
? <a href={item.href}>{item.label}</a>
|
|
51
|
+
: null
|
|
52
|
+
))}
|
|
53
|
+
</nav>
|
|
54
|
+
{config.features.darkMode && <theme-toggle></theme-toggle>}
|
|
55
|
+
</header>
|
|
56
|
+
<main class="edoxen-main">
|
|
57
|
+
<slot />
|
|
58
|
+
</main>
|
|
59
|
+
<footer class="edoxen-footer">
|
|
60
|
+
{config.theme.logos.footer && (
|
|
61
|
+
<img src={config.theme.logos.footer} alt="" />
|
|
62
|
+
)}
|
|
63
|
+
<ul class="edoxen-footer__social">
|
|
64
|
+
{config.social.map((s) => (
|
|
65
|
+
<li><a href={s.href}>{s.label}</a></li>
|
|
66
|
+
))}
|
|
67
|
+
</ul>
|
|
68
|
+
<p>Powered by <a href="https://edoxen.github.io">Edoxen</a>.</p>
|
|
69
|
+
</footer>
|
|
70
|
+
<script>
|
|
71
|
+
import '../../islands/theme-toggle.ts'
|
|
72
|
+
import '../../islands/urn-copy.ts'
|
|
73
|
+
import '../../islands/search-filter.ts'
|
|
74
|
+
import '../../islands/section-tabs.ts'
|
|
75
|
+
import '../../islands/decade-scroller.ts'
|
|
76
|
+
import '../../islands/print-button.ts'
|
|
77
|
+
</script>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
import BaseLayout from './BaseLayout.astro'
|
|
3
|
+
import type { EdoxenConfig } from '../../config/index.js'
|
|
4
|
+
import type { DecisionListItem } from '../../data/index.js'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
config: EdoxenConfig
|
|
8
|
+
item: DecisionListItem
|
|
9
|
+
}
|
|
10
|
+
const { config, item } = Astro.props
|
|
11
|
+
const title = `${item.title} — ${config.site.title}`
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<BaseLayout config={config} title={title} description={item.kind}>
|
|
15
|
+
<article class="edoxen-decision">
|
|
16
|
+
<a href="/decisions">← Decisions</a>
|
|
17
|
+
<h1>{item.title}</h1>
|
|
18
|
+
<p><code>{item.urn}</code></p>
|
|
19
|
+
<slot />
|
|
20
|
+
</article>
|
|
21
|
+
</BaseLayout>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
import BaseLayout from './BaseLayout.astro'
|
|
3
|
+
import type { EdoxenConfig } from '../../config/index.js'
|
|
4
|
+
import type { MeetingListItem } from '../../data/index.js'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
config: EdoxenConfig
|
|
8
|
+
item: MeetingListItem
|
|
9
|
+
}
|
|
10
|
+
const { config, item } = Astro.props
|
|
11
|
+
const title = `${item.title} — ${config.site.title}`
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<BaseLayout config={config} title={title} description={item.identifier}>
|
|
15
|
+
<article class="edoxen-meeting">
|
|
16
|
+
<a href="/meetings">← Meetings</a>
|
|
17
|
+
<h1>{item.title}</h1>
|
|
18
|
+
{item.startDate && <p>{item.startDate}{item.endDate && ` – ${item.endDate}`}</p>}
|
|
19
|
+
<slot />
|
|
20
|
+
</article>
|
|
21
|
+
</BaseLayout>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
import cfg from 'virtual:edoxen-config'
|
|
3
|
+
import BaseLayout from '../layouts/BaseLayout.astro'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<BaseLayout config={cfg} title={`Not found — ${cfg.site.title}`}>
|
|
7
|
+
<h1>404</h1>
|
|
8
|
+
<p>The page you requested does not exist.</p>
|
|
9
|
+
<p><a href="/">Return to {cfg.site.title}</a></p>
|
|
10
|
+
</BaseLayout>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
import cfg from 'virtual:edoxen-config'
|
|
3
|
+
import payloads from 'virtual:edoxen-payloads'
|
|
4
|
+
import BaseLayout from '../../layouts/BaseLayout.astro'
|
|
5
|
+
|
|
6
|
+
export function getStaticPaths() {
|
|
7
|
+
return cfg.locales
|
|
8
|
+
.filter((l) => (l.routePrefix ?? '') !== '')
|
|
9
|
+
.map((l) => ({ params: { lang: l.code } }))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const decisionCount = payloads.decisionsList.items.length
|
|
13
|
+
const meetingCount = payloads.meetingsList.items.length
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<BaseLayout config={cfg} title={`About — ${cfg.site.title}`}>
|
|
17
|
+
<h1>About</h1>
|
|
18
|
+
|
|
19
|
+
<section>
|
|
20
|
+
<h2>{cfg.site.title}</h2>
|
|
21
|
+
<p>{cfg.site.description}</p>
|
|
22
|
+
<dl class="edoxen-stats">
|
|
23
|
+
<div><dt>Decisions</dt><dd>{decisionCount}</dd></div>
|
|
24
|
+
<div><dt>Meetings</dt><dd>{meetingCount}</dd></div>
|
|
25
|
+
</dl>
|
|
26
|
+
</section>
|
|
27
|
+
|
|
28
|
+
<section>
|
|
29
|
+
<h2>Edoxen format</h2>
|
|
30
|
+
<p>
|
|
31
|
+
This site renders meeting and decision data using the
|
|
32
|
+
<a href="https://edoxen.github.io">Edoxen</a> information model — a
|
|
33
|
+
YAML-based schema for formal proceedings of standards bodies.
|
|
34
|
+
</p>
|
|
35
|
+
</section>
|
|
36
|
+
|
|
37
|
+
<section>
|
|
38
|
+
<h2>Using this site</h2>
|
|
39
|
+
<ul>
|
|
40
|
+
<li><strong>Decisions</strong> — browse the resolutions archive.</li>
|
|
41
|
+
<li><strong>Meetings</strong> — see meetings and their agendas, minutes, and adopted decisions.</li>
|
|
42
|
+
<li><strong>URNs</strong> — every entity has a stable URN for citation.</li>
|
|
43
|
+
</ul>
|
|
44
|
+
</section>
|
|
45
|
+
</BaseLayout>
|
|
46
|
+
|
|
47
|
+
<style>
|
|
48
|
+
.edoxen-stats {
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-wrap: wrap;
|
|
51
|
+
gap: 1.5rem;
|
|
52
|
+
margin: 1rem 0 0;
|
|
53
|
+
}
|
|
54
|
+
.edoxen-stats dd {
|
|
55
|
+
margin: 0;
|
|
56
|
+
font-size: 1.5rem;
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
}
|
|
59
|
+
.edoxen-stats dt {
|
|
60
|
+
font-size: 0.85rem;
|
|
61
|
+
color: var(--edoxen-color-muted);
|
|
62
|
+
text-transform: uppercase;
|
|
63
|
+
letter-spacing: 0.05em;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
import cfg from 'virtual:edoxen-config'
|
|
3
|
+
import payloads from 'virtual:edoxen-payloads'
|
|
4
|
+
import DecisionLayout from '../../../layouts/DecisionLayout.astro'
|
|
5
|
+
import UrnBar from '../../../components/UrnBar.astro'
|
|
6
|
+
import BodyBadge from '../../../components/BodyBadge.astro'
|
|
7
|
+
import LocaleTabs from '../../../components/LocaleTabs.astro'
|
|
8
|
+
import { decisionJsonLd } from '../../../../seo/index.js'
|
|
9
|
+
import { pickLocalizedValue, availableSpellings } from '../../../../i18n/index.js'
|
|
10
|
+
|
|
11
|
+
export function getStaticPaths() {
|
|
12
|
+
const urns = Object.keys(payloads.decisionByUrn)
|
|
13
|
+
const nonDefault = cfg.locales.filter((l) => (l.routePrefix ?? '') !== '')
|
|
14
|
+
return urns.flatMap((urn) =>
|
|
15
|
+
nonDefault.map((loc) => ({
|
|
16
|
+
params: { lang: loc.code, urn },
|
|
17
|
+
props: { decision: payloads.decisionByUrn[urn] },
|
|
18
|
+
})),
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface Props {
|
|
23
|
+
decision: typeof payloads.decisionByUrn[string]
|
|
24
|
+
}
|
|
25
|
+
const { decision } = Astro.props
|
|
26
|
+
const locale = Astro.params.lang ?? cfg.site.locale
|
|
27
|
+
const title = pickLocalizedValue(decision.title, locale, decision.urn ?? '')
|
|
28
|
+
const subject = pickLocalizedValue(decision.subject, locale)
|
|
29
|
+
const considering = pickLocalizedValue(decision.considering, locale)
|
|
30
|
+
const actions = decision.actions ?? []
|
|
31
|
+
const considerations = decision.considerations ?? []
|
|
32
|
+
const approvals = decision.approvals ?? []
|
|
33
|
+
const titleSpellings = availableSpellings(decision.title)
|
|
34
|
+
const jsonld = decisionJsonLd(decision, { siteUrl: cfg.site.url, siteTitle: cfg.site.title, defaultLocale: locale })
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
<DecisionLayout
|
|
38
|
+
config={cfg}
|
|
39
|
+
item={{
|
|
40
|
+
urn: decision.urn ?? '',
|
|
41
|
+
identifier: (decision.identifier[0]?.prefix ?? '') + '-' + (decision.identifier[0]?.number ?? ''),
|
|
42
|
+
title,
|
|
43
|
+
kind: decision.kind ?? '',
|
|
44
|
+
dates: (decision.dates ?? []).map((d) => ({ type: d.type ?? '', date: d.date ?? '' })),
|
|
45
|
+
bodyType: decision.body_type,
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
<script type="application/ld+json" set:html={JSON.stringify(jsonld)} is:inline />
|
|
49
|
+
<UrnBar urn={decision.urn ?? ''} />
|
|
50
|
+
|
|
51
|
+
{decision.body_type && <p><BodyBadge code={decision.body_type} /></p>}
|
|
52
|
+
|
|
53
|
+
{titleSpellings.length > 1 && <LocaleTabs spellings={titleSpellings} active={locale} field="title" />}
|
|
54
|
+
|
|
55
|
+
{subject && (
|
|
56
|
+
<section>
|
|
57
|
+
<h2>Subject</h2>
|
|
58
|
+
<p>{subject}</p>
|
|
59
|
+
</section>
|
|
60
|
+
)}
|
|
61
|
+
|
|
62
|
+
{considering && (
|
|
63
|
+
<section>
|
|
64
|
+
<h2>Considering</h2>
|
|
65
|
+
<p>{considering}</p>
|
|
66
|
+
</section>
|
|
67
|
+
)}
|
|
68
|
+
|
|
69
|
+
{considerations.length > 0 && (
|
|
70
|
+
<section>
|
|
71
|
+
<h2>Considerations</h2>
|
|
72
|
+
<ul>
|
|
73
|
+
{considerations.map((c) => (
|
|
74
|
+
<li>{c.kind}{c.references && c.references.length > 0 ? `: ${c.references.map((r) => r.urn).join(', ')}` : ''}</li>
|
|
75
|
+
))}
|
|
76
|
+
</ul>
|
|
77
|
+
</section>
|
|
78
|
+
)}
|
|
79
|
+
|
|
80
|
+
{actions.length > 0 && (
|
|
81
|
+
<section>
|
|
82
|
+
<h2>Actions</h2>
|
|
83
|
+
<ul>
|
|
84
|
+
{actions.map((a) => (
|
|
85
|
+
<li>
|
|
86
|
+
<strong>{a.type}</strong>
|
|
87
|
+
{a.message && <div>{pickLocalizedValue(a.message, locale)}</div>}
|
|
88
|
+
</li>
|
|
89
|
+
))}
|
|
90
|
+
</ul>
|
|
91
|
+
</section>
|
|
92
|
+
)}
|
|
93
|
+
|
|
94
|
+
{approvals.length > 0 && (
|
|
95
|
+
<section>
|
|
96
|
+
<h2>Approvals</h2>
|
|
97
|
+
<ul>
|
|
98
|
+
{approvals.map((a) => (
|
|
99
|
+
<li>{a.degree}{a.body ? ` — ${a.body}` : ''}{a.date ? ` (${a.date})` : ''}</li>
|
|
100
|
+
))}
|
|
101
|
+
</ul>
|
|
102
|
+
</section>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
{(decision.dates ?? []).length > 0 && (
|
|
106
|
+
<section>
|
|
107
|
+
<h2>Dates</h2>
|
|
108
|
+
<ul>
|
|
109
|
+
{(decision.dates ?? []).map((d) => (
|
|
110
|
+
<li>{d.type}: {d.date}</li>
|
|
111
|
+
))}
|
|
112
|
+
</ul>
|
|
113
|
+
</section>
|
|
114
|
+
)}
|
|
115
|
+
|
|
116
|
+
{(decision.urls ?? []).length > 0 && (
|
|
117
|
+
<section>
|
|
118
|
+
<h2>Reference documents</h2>
|
|
119
|
+
<ul>
|
|
120
|
+
{(decision.urls ?? []).map((u) => (
|
|
121
|
+
<li><a href={u.ref}>{u.kind ?? 'document'}</a></li>
|
|
122
|
+
))}
|
|
123
|
+
</ul>
|
|
124
|
+
</section>
|
|
125
|
+
)}
|
|
126
|
+
</DecisionLayout>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import cfg from 'virtual:edoxen-config'
|
|
3
|
+
import payloads from 'virtual:edoxen-payloads'
|
|
4
|
+
import BaseLayout from '../../../layouts/BaseLayout.astro'
|
|
5
|
+
import DecisionList from '../../../components/DecisionList.astro'
|
|
6
|
+
|
|
7
|
+
export function getStaticPaths() {
|
|
8
|
+
return cfg.locales
|
|
9
|
+
.filter((l) => (l.routePrefix ?? '') !== '')
|
|
10
|
+
.map((l) => ({ params: { lang: l.code } }))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const lang = Astro.params.lang ?? cfg.site.locale
|
|
14
|
+
const locEntry = cfg.locales.find((l) => l.code === lang)
|
|
15
|
+
const basePath = locEntry?.routePrefix ? `${locEntry.routePrefix}/decisions` : '/decisions'
|
|
16
|
+
const items = payloads.decisionsList.items
|
|
17
|
+
const endpoint = `${cfg.site.basePath}data/decisions.json`.replace(/\/+/g, '/')
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<BaseLayout config={cfg} lang={lang} title={`Decisions — ${cfg.site.title}`}>
|
|
21
|
+
<h1>Decisions</h1>
|
|
22
|
+
|
|
23
|
+
<search-filter data-endpoint={endpoint} data-base-path={basePath}></search-filter>
|
|
24
|
+
<noscript>
|
|
25
|
+
<DecisionList items={items} />
|
|
26
|
+
</noscript>
|
|
27
|
+
</BaseLayout>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
import cfg from 'virtual:edoxen-config'
|
|
3
|
+
import payloads from 'virtual:edoxen-payloads'
|
|
4
|
+
import BaseLayout from '../../layouts/BaseLayout.astro'
|
|
5
|
+
|
|
6
|
+
export function getStaticPaths() {
|
|
7
|
+
return cfg.locales
|
|
8
|
+
.filter((l) => (l.routePrefix ?? '') !== '')
|
|
9
|
+
.map((l) => ({ params: { lang: l.code } }))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const lang = Astro.params.lang ?? cfg.site.locale
|
|
13
|
+
const locEntry = cfg.locales.find((l) => l.code === lang)
|
|
14
|
+
const urlPrefix = locEntry?.routePrefix ?? ''
|
|
15
|
+
const recentDecisions = payloads.decisionsList.items.slice(0, 5)
|
|
16
|
+
const recentMeetings = payloads.meetingsList.items.slice(0, 3)
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<BaseLayout config={cfg} lang={lang}>
|
|
20
|
+
<section>
|
|
21
|
+
<h1>{cfg.site.title}</h1>
|
|
22
|
+
<p>{cfg.site.description}</p>
|
|
23
|
+
</section>
|
|
24
|
+
|
|
25
|
+
<section>
|
|
26
|
+
<h2>Latest decisions</h2>
|
|
27
|
+
<ul>
|
|
28
|
+
{recentDecisions.map((d) => (
|
|
29
|
+
<li>
|
|
30
|
+
<a href={`${urlPrefix}/decisions/${encodeURIComponent(d.urn)}`}>{d.title}</a>
|
|
31
|
+
{d.bodyType && <span class="edoxen-badge">{d.bodyType}</span>}
|
|
32
|
+
</li>
|
|
33
|
+
))}
|
|
34
|
+
</ul>
|
|
35
|
+
</section>
|
|
36
|
+
|
|
37
|
+
{recentMeetings.length > 0 && (
|
|
38
|
+
<section>
|
|
39
|
+
<h2>Recent meetings</h2>
|
|
40
|
+
<ul>
|
|
41
|
+
{recentMeetings.map((m) => (
|
|
42
|
+
<li>
|
|
43
|
+
<a href={`${urlPrefix}/meetings/${encodeURIComponent(m.urn)}`}>{m.title}</a>
|
|
44
|
+
{m.startDate && <span> — {m.startDate}</span>}
|
|
45
|
+
</li>
|
|
46
|
+
))}
|
|
47
|
+
</ul>
|
|
48
|
+
</section>
|
|
49
|
+
)}
|
|
50
|
+
</BaseLayout>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
import cfg from 'virtual:edoxen-config'
|
|
3
|
+
import payloads from 'virtual:edoxen-payloads'
|
|
4
|
+
import MeetingLayout from '../../../layouts/MeetingLayout.astro'
|
|
5
|
+
import UrnBar from '../../../components/UrnBar.astro'
|
|
6
|
+
import BodyBadge from '../../../components/BodyBadge.astro'
|
|
7
|
+
import AgendaTable from '../../../components/AgendaTable.astro'
|
|
8
|
+
import MinutesSection from '../../../components/MinutesSection.astro'
|
|
9
|
+
import { meetingJsonLd } from '../../../../seo/index.js'
|
|
10
|
+
import { pickLocalizedValue } from '../../../../i18n/index.js'
|
|
11
|
+
|
|
12
|
+
export function getStaticPaths() {
|
|
13
|
+
const urns = Object.keys(payloads.meetingByUrn)
|
|
14
|
+
const nonDefault = cfg.locales.filter((l) => (l.routePrefix ?? '') !== '')
|
|
15
|
+
return urns.flatMap((urn) =>
|
|
16
|
+
nonDefault.map((loc) => ({
|
|
17
|
+
params: { lang: loc.code, urn },
|
|
18
|
+
props: { meeting: payloads.meetingByUrn[urn] },
|
|
19
|
+
})),
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
meeting: typeof payloads.meetingByUrn[string]
|
|
25
|
+
}
|
|
26
|
+
const { meeting } = Astro.props
|
|
27
|
+
const locale = Astro.params.lang ?? cfg.site.locale
|
|
28
|
+
const title = pickLocalizedValue(meeting.title, locale, meeting.urn ?? '')
|
|
29
|
+
const officers = meeting.officers ?? []
|
|
30
|
+
const agenda = meeting.agenda
|
|
31
|
+
const minutes = meeting.minutes ?? []
|
|
32
|
+
const linkedDecisions = (meeting.decisions ?? []).map((id) =>
|
|
33
|
+
payloads.decisionsList.items.find((d) =>
|
|
34
|
+
d.identifier === `${id.prefix}-${id.number}`
|
|
35
|
+
)
|
|
36
|
+
).filter((d): d is NonNullable<typeof d> => d !== undefined)
|
|
37
|
+
const jsonld = meetingJsonLd(meeting, { siteUrl: cfg.site.url, siteTitle: cfg.site.title, defaultLocale: locale })
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<MeetingLayout
|
|
41
|
+
config={cfg}
|
|
42
|
+
item={{
|
|
43
|
+
urn: meeting.urn ?? '',
|
|
44
|
+
identifier: (meeting.identifier[0]?.prefix ?? '') + '-' + (meeting.identifier[0]?.number ?? ''),
|
|
45
|
+
title,
|
|
46
|
+
startDate: meeting.date_range?.start,
|
|
47
|
+
endDate: meeting.date_range?.end,
|
|
48
|
+
bodyType: meeting.body_type,
|
|
49
|
+
city: meeting.city,
|
|
50
|
+
countryCode: meeting.country_code,
|
|
51
|
+
status: meeting.status,
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
<script type="application/ld+json" set:html={JSON.stringify(jsonld)} is:inline />
|
|
55
|
+
<UrnBar urn={meeting.urn ?? ''} />
|
|
56
|
+
|
|
57
|
+
{meeting.body_type && <p><BodyBadge code={meeting.body_type} /></p>}
|
|
58
|
+
|
|
59
|
+
{meeting.city && (
|
|
60
|
+
<section>
|
|
61
|
+
<h2>Venue</h2>
|
|
62
|
+
<p>{meeting.city}{meeting.country_code ? `, ${meeting.country_code}` : ''}</p>
|
|
63
|
+
</section>
|
|
64
|
+
)}
|
|
65
|
+
|
|
66
|
+
{officers.length > 0 && (
|
|
67
|
+
<section>
|
|
68
|
+
<h2>Officers</h2>
|
|
69
|
+
<ul>
|
|
70
|
+
{officers.map((o) => (
|
|
71
|
+
<li>{o.role}{o.person?.name ? `: ${o.person.name}` : ''}</li>
|
|
72
|
+
))}
|
|
73
|
+
</ul>
|
|
74
|
+
</section>
|
|
75
|
+
)}
|
|
76
|
+
|
|
77
|
+
{agenda && agenda.items && agenda.items.length > 0 && (
|
|
78
|
+
<section>
|
|
79
|
+
<h2>Agenda</h2>
|
|
80
|
+
<AgendaTable items={agenda.items} locale={locale} />
|
|
81
|
+
</section>
|
|
82
|
+
)}
|
|
83
|
+
|
|
84
|
+
{minutes.length > 0 && (
|
|
85
|
+
<section>
|
|
86
|
+
<h2>Minutes</h2>
|
|
87
|
+
{minutes.map((m) => (
|
|
88
|
+
<MinutesSection minutes={m} locale={locale} />
|
|
89
|
+
))}
|
|
90
|
+
</section>
|
|
91
|
+
)}
|
|
92
|
+
|
|
93
|
+
{linkedDecisions.length > 0 && (
|
|
94
|
+
<section>
|
|
95
|
+
<h2>Adopted decisions</h2>
|
|
96
|
+
<ul>
|
|
97
|
+
{linkedDecisions.map((d) => (
|
|
98
|
+
<li><a href={`/decisions/${encodeURIComponent(d.urn)}`}>{d.title}</a></li>
|
|
99
|
+
))}
|
|
100
|
+
</ul>
|
|
101
|
+
</section>
|
|
102
|
+
)}
|
|
103
|
+
|
|
104
|
+
{(meeting.source_urls ?? []).length > 0 && (
|
|
105
|
+
<section>
|
|
106
|
+
<h2>Source documents</h2>
|
|
107
|
+
<ul>
|
|
108
|
+
{(meeting.source_urls ?? []).map((s) => (
|
|
109
|
+
<li><a href={s.ref}>{s.format ?? 'document'}</a></li>
|
|
110
|
+
))}
|
|
111
|
+
</ul>
|
|
112
|
+
</section>
|
|
113
|
+
)}
|
|
114
|
+
</MeetingLayout>
|