3 Commits
Author SHA1 Message Date
zemion f8ab0f0e8d fix(packaging): expose immutable WebUI Git package for v0.1.21
Module Package Release / publish-packages (push) Successful in 11s
2026-09-08 02:06:11 +02:00
zemion 05c8c97044 Release govoplan-voting v0.1.21: unify interface contracts and documentation 2026-09-08 01:32:54 +02:00
zemion 00650aee81 docs: complete German structured documentation
Module Package Release / publish-packages (push) Successful in 11s
2026-08-24 01:15:42 +02:00
8 changed files with 157 additions and 17 deletions
+18
View File
@@ -26,3 +26,21 @@ profile and integration prerequisites are documented in
[the POLYAS provider profile](docs/POLYAS_PROVIDER_PROFILE.md). Development of
a native certifiable provider follows the staged, independently evaluated
[certifiable Voting program](docs/CERTIFIABLE_VOTING_PROGRAM.md).
## Git-source WebUI package
The repository root exposes `@govoplan/voting-webui` for Git-tagged release
dependencies. It mirrors the owning `webui/package.json` version, public
TypeScript/CSS exports and peer requirements, with entry paths under
`webui/src`. Consumers provide the shared Core/React peers; the facade runs no
development or install scripts. The source archive contains `webui/src`, this
README and any repository license file. Run module development checks from `webui/`; Python
installation remains governed by `pyproject.toml`.
Das Repository stellt `@govoplan/voting-webui` am Wurzelpfad für versionierte
Git-Abhängigkeiten bereit. Version, öffentliche TypeScript-/CSS-Exporte und
Peer-Anforderungen entsprechen `webui/package.json`; die Einstiegspfade liegen
unter `webui/src`. Gemeinsame Core-/React-Peers stellt die einbindende Anwendung
bereit. Die Fassade führt keine Entwicklungs- oder Installationsskripte aus.
Entwicklungsprüfungen bleiben in `webui/`, die Python-Installation weiterhin in
`pyproject.toml` definiert.
+32
View File
@@ -0,0 +1,32 @@
{
"name": "@govoplan/voting-webui",
"version": "0.1.21",
"private": true,
"type": "module",
"main": "webui/src/index.ts",
"module": "webui/src/index.ts",
"types": "webui/src/index.ts",
"exports": {
".": {
"types": "./webui/src/index.ts",
"import": "./webui/src/index.ts"
},
"./styles/voting.css": "./webui/src/styles/voting.css"
},
"peerDependencies": {
"@govoplan/core-webui": "^0.1.45",
"lucide-react": "^1.23.0",
"react": ">=19.2.7 <20",
"react-dom": ">=19.2.7 <20"
},
"peerDependenciesMeta": {
"@govoplan/core-webui": {
"optional": true
}
},
"files": [
"webui/src",
"README.md",
"LICENSE"
]
}
+2 -2
View File
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
[project]
name = "govoplan-voting"
version = "0.1.19"
version = "0.1.21"
description = "Governed voting, ballot assurance, tally, and certification for GovOPlaN."
readme = "README.md"
requires-python = ">=3.12"
authors = [{ name = "GovOPlaN" }]
dependencies = ["govoplan-core>=0.1.18", "govoplan-access>=0.1.18"]
dependencies = ["govoplan-core>=0.1.45", "govoplan-access>=0.1.18"]
[tool.setuptools.packages.find]
where = ["src"]
+1 -1
View File
@@ -1,3 +1,3 @@
"""GovOPlaN Voting module."""
__version__ = "0.1.19"
__version__ = "0.1.21"
@@ -0,0 +1,67 @@
"""German translations for public structured documentation metadata."""
from __future__ import annotations
from typing import Any
GERMAN_STRUCTURED_TRANSLATIONS: dict[str, dict[str, Any]] = {'voting.assurance': {'privacy_notes': ['Aufgezeichnete Stimmzettel sind rekonstruierbar und '
'dürfen niemals als geheim bezeichnet werden.',
'Von Anbietern unterstützte Profile zeigen nur das '
'Aggregat, den Empfang, den Hash und die Nachweise, die '
'der Anbietervertrag erlaubt.',
'Der gebündelte lokale vertrauliche Anbieter ist '
'serverlesbar und unzertifiziert trotz verschlüsselter '
'gespeicherter Auswahl.']},
'voting.data-subject-requests': {'consequence_classes': {'export_confidential_receipt': 'Gibt '
'Teilnahme-Metadaten '
'ohne '
'Auswahl '
'oder '
'Geheimtext '
'zurück.',
'export_recorded_vote': 'Gibt die '
'rekonstruierbaren '
'aufgezeichneten '
'Auswahlen des '
'Subjekts '
'zurück.',
'retain_ballot_evidence': 'Bewahrt die '
'Integrität '
'der '
'Stimmzettel '
'und die '
'Geschichte '
'der '
'Herausforderungen.'}},
'voting.reference.fields-and-consequences': {'consequence_classes': {'cast': 'Registriert oder '
'ersetzt eine '
'autorisierte Stimme '
'und gibt eine '
'datenschutzbeschränkte '
'Quittung zurück.',
'certify': 'Fügt '
'Zertifizierungsnachweise '
'hinzu, ohne das '
'eingefrorene '
'Ergebnis neu zu '
'schreiben.',
'challenge_or_annul': 'Hängt '
'einen '
'begründeten '
'Governance-Übergang '
'an, '
'während '
'vorherige '
'Nachweise '
'beibehalten '
'werden.',
'close': 'Stoppt das Gießen '
'und zeichnet die '
'Aggregatzahl auf.',
'open': 'Friert die '
'Stimmzetteldefinition '
'und Wählerschaft '
'ein und erlaubt '
'autorisiertes '
'Casting.'}}}
+24 -1
View File
@@ -1,5 +1,8 @@
from __future__ import annotations
from govoplan_core.core.modules import with_documentation_structured_translations
from govoplan_voting.backend.german_structured_documentation import GERMAN_STRUCTURED_TRANSLATIONS
from pathlib import Path
from govoplan_core.core.access import (
@@ -52,7 +55,7 @@ from govoplan_voting.backend.local_confidential_provider import (
MODULE_ID = "voting"
MODULE_NAME = "Voting"
MODULE_VERSION = "0.1.19"
MODULE_VERSION = "0.1.21"
READ_SCOPE = "voting:ballot:read"
MANAGE_SCOPE = "voting:ballot:manage"
CAST_SCOPE = "voting:ballot:cast"
@@ -320,6 +323,21 @@ manifest = ModuleManifest(
),
tenant_summary_providers=(_tenant_summary,),
documentation=(
DocumentationTopic(
id="voting.workspace-layout",
title="Voting workspace layout",
summary="Find workspace actions and read consistently arranged content.",
body="Reload and New ballot remain in the workspace-wide top bar, even when a ballot is selected or the catalogue is empty. Reload sits immediately before New. Ballot actions such as opening, casting, certification, challenge, and annulment stay with the selected ballot. New ballot stays visible but disabled without management permission. The shared layout changes no electorate or assurance policy; administrators retain the existing separate management, casting, certification, and administrative grants.",
layer="static",
documentation_types=("user", "admin"),
audience=("user", "module_admin", "operator"),
order=5,
translations={"de": {
"title": "Abstimmungen: Aufbau des Arbeitsbereichs",
"summary": "Arbeitsbereichsaktionen finden und einheitlich angeordnete Inhalte lesen.",
"body": "Neu laden und Neue Abstimmung bleiben in der arbeitsbereichsweiten oberen Leiste, auch bei ausgewählter Abstimmung oder leerem Katalog. Neu laden steht unmittelbar vor Neu. Öffnen, Stimmabgabe, Zertifizierung, Anfechtung und Annullierung bleiben bei der ausgewählten Abstimmung. Neue Abstimmung bleibt ohne Verwaltungsrecht sichtbar, aber deaktiviert. Das gemeinsame Layout verändert weder Wählerschaft noch Zusicherung; die getrennten Verwaltungs-, Stimmabgabe-, Zertifizierungs- und Administrationsrechte bleiben bestehen.",
}},
),
DocumentationTopic(
id="voting.data-subject-requests",
title="Voting data-subject requests",
@@ -567,5 +585,10 @@ manifest = ModuleManifest(
)
manifest = with_documentation_structured_translations(
manifest, locale="de", translations=GERMAN_STRUCTURED_TRANSLATIONS
)
def get_manifest() -> ModuleManifest:
return manifest
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@govoplan/voting-webui",
"version": "0.1.19",
"version": "0.1.21",
"private": true,
"type": "module",
"main": "src/index.ts",
@@ -14,7 +14,7 @@
"./styles/voting.css": "./src/styles/voting.css"
},
"peerDependencies": {
"@govoplan/core-webui": "^0.1.18",
"@govoplan/core-webui": "^0.1.45",
"lucide-react": "^1.23.0",
"react": ">=19.2.7 <20",
"react-dom": ">=19.2.7 <20"
+11 -11
View File
@@ -18,6 +18,7 @@ import { ActionBlockerHint,
StatePanel,
StatusBadge,
WorkspaceActionBar,
WorkspaceFrame,
WorkspaceLayout,
hasScope,
i18nMessage,
@@ -150,7 +151,15 @@ export default function VotingPage({ settings, auth }: PlatformRouteContext) {
}
return (
<main className="voting-page">
<WorkspaceFrame as="main" className="voting-page" label="Ballots" interfaceId="voting.workspace" helpContextId="voting.workspace" helpModuleId="voting">
<WorkspaceActionBar
scope="workspace"
variant="collection"
refreshable
reloadAction={{ onReload: () => void loadList().catch((reason) => setError(message(reason, "Ballots could not be loaded."))), loading: loading || busy, label: "Refresh ballots" }}
createAction={<Button variant="primary" disabled={!canManage || busy} disabledReason={busy ? VOTING_I18N.busy : !canManage ? VOTING_I18N.manageReason : undefined} onClick={() => setEditing("new")}><Plus size={16} aria-hidden="true" />New ballot</Button>}
helpAction={<DocumentationHelpLink reference={VOTING_DOCUMENTATION} />}
/>
<WorkspaceLayout
variant="split"
primarySize="default"
@@ -165,15 +174,6 @@ export default function VotingPage({ settings, auth }: PlatformRouteContext) {
helpContextId="voting.workspace"
helpModuleId="voting"
primary={<>
<WorkspaceActionBar
scope="collection-pane"
variant="collection"
refreshable
reloadAction={{ onReload: () => void loadList(), loading: loading || busy, label: "Refresh ballots" }}
className="voting-toolbar"
createAction={<Button variant="primary" disabled={!canManage} disabledReason={!canManage ? VOTING_I18N.manageReason : undefined} onClick={() => setEditing("new")}><Plus size={16} aria-hidden="true" />New ballot</Button>}
helpAction={<DocumentationHelpLink reference={VOTING_DOCUMENTATION} />}
/>
<PageScrollViewport className="voting-list-viewport">
{loading && <LoadingIndicator label="Loading ballots" />}
{!loading && items.length === 0 && <StatePanel size="compact" description="No ballots." />}
@@ -317,7 +317,7 @@ export default function VotingPage({ settings, auth }: PlatformRouteContext) {
void cast();
}}
/>
</main>
</WorkspaceFrame>
);
}