Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a7bd5b3b4 | ||
|
|
00bec0ed94 | ||
|
|
cfcd723496 | ||
|
|
06afa79795 |
@@ -39,3 +39,21 @@ PYTHONPATH=src:/mnt/DATA/git/govoplan-core/src \
|
||||
```
|
||||
|
||||
See [docs/FORMS_BOUNDARY.md](docs/FORMS_BOUNDARY.md) for the boundary decision.
|
||||
|
||||
## Git-source WebUI package
|
||||
|
||||
The repository root exposes `@govoplan/forms-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/forms-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.
|
||||
|
||||
@@ -70,6 +70,10 @@ Form definitions should carry:
|
||||
option, constraint, draft, attachment, signature, policy, and handoff editing.
|
||||
- Forms Runtime performs value validation against the resolved definition; the
|
||||
definition owner does not persist submissions.
|
||||
- Forms consumes Core's title-help and text-help layout contracts for its
|
||||
catalogue and revision dialog. Workflow books sit beside their heading;
|
||||
semantic documentation stays beside the Form meaning label for the exact
|
||||
definition. The placement contract does not move content ownership to Core.
|
||||
|
||||
## Recovery And Operations
|
||||
|
||||
|
||||
@@ -36,3 +36,9 @@ shared section-header toolbar surface. Core therefore owns viewport, search,
|
||||
state, heading, and narrow-layout geometry. These contracts do not change
|
||||
filter scope, permissions, revision semantics, or publication consequences.
|
||||
English and German catalogues cover the owned route and editor vocabulary.
|
||||
|
||||
Documentation books belong immediately beside their contextual text. The
|
||||
catalogue uses Core's workspace title and `titleHelp`; the revision dialog uses
|
||||
its title help, and the separate Form meaning label carries semantic help for
|
||||
that exact definition. Field help stays beside its label. This uses Core layout
|
||||
contracts and does not change revision, publication, or runtime ownership.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@govoplan/forms-webui",
|
||||
"version": "0.1.23",
|
||||
"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/forms.css": "./webui/src/styles/forms.css"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
"react-router": ">=8.3.0 <9"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@govoplan/core-webui": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"webui/src",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
]
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "govoplan-forms"
|
||||
version = "0.1.20"
|
||||
version = "0.1.23"
|
||||
description = "Immutable reusable form definitions for GovOPlaN."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""GovOPlaN Forms module."""
|
||||
|
||||
__version__ = "0.1.20"
|
||||
__version__ = "0.1.23"
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
"""German translations for public structured documentation metadata."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
GERMAN_STRUCTURED_TRANSLATIONS: dict[str, dict[str, Any]] = {'forms.data-subject-requests': {'consequence_classes': {'exclude_form_semantics': 'Gibt keinen '
|
||||
'Schema-, Feld- '
|
||||
'oder '
|
||||
'Submission-Value-Inhalt '
|
||||
'zurück.',
|
||||
'export_definition_attribution': 'Returns '
|
||||
'minimierten '
|
||||
'die '
|
||||
'unveränderliche '
|
||||
'Revisionsaktivität.'}},
|
||||
'forms.definitions': {'privacy_notes': ['Definitionskataloge enthalten Schemata und '
|
||||
'Richtlinienreferenzen, keine eingereichten '
|
||||
'Formularwerte.',
|
||||
'Die Paketbewertung gewährt keinen Zugriff auf '
|
||||
'referenzierte Laufzeiteinreichungen oder externe '
|
||||
'Anbieter.',
|
||||
'Veröffentlichte Zugänglichkeits- und '
|
||||
'Lokalisierungsinhalte sind überall dort sichtbar, wo die '
|
||||
'genaue Definition autorisiert ist.']},
|
||||
'forms.reference.fields-and-consequences': {'consequence_classes': {'import_package': 'Erstellt '
|
||||
'einen '
|
||||
'lokalen '
|
||||
'Entwurf '
|
||||
'und behält '
|
||||
'die '
|
||||
'Herkunft '
|
||||
'des Pakets '
|
||||
'ohne '
|
||||
'automatische '
|
||||
'Veröffentlichung.',
|
||||
'publish': 'Macht die genaue '
|
||||
'Revision für '
|
||||
'zukünftige '
|
||||
'autorisierte '
|
||||
'Instanzen '
|
||||
'verfügbar.',
|
||||
'retire': 'Stoppt die '
|
||||
'zukünftige '
|
||||
'Nutzung, während '
|
||||
'Definitionen und '
|
||||
'genaue '
|
||||
'Laufzeitreferenzen '
|
||||
'beibehalten '
|
||||
'werden.',
|
||||
'save_revision': 'Erstellt '
|
||||
'eine '
|
||||
'unveränderliche '
|
||||
'Definitionsrevision '
|
||||
'mit einem '
|
||||
'Änderungsgrund.'},
|
||||
'limitations': ['Der Paketimport veröffentlicht '
|
||||
'niemals automatisch eine '
|
||||
'Formularrevision.',
|
||||
'Laufzeiteingaben und eingereichte '
|
||||
'Werte werden niemals als '
|
||||
'Definitionskonfiguration exportiert.',
|
||||
'Generische Paket-Rollback erfordert '
|
||||
'die Voranwendung Datenbank Snapshot '
|
||||
'beibehalten.'],
|
||||
'operational_consequences': ['Preserve blockiert eine '
|
||||
'widersprüchliche lokale '
|
||||
'Definition; new '
|
||||
'revision muss explizit '
|
||||
'überprüft werden.',
|
||||
'Das erneute Anwenden '
|
||||
'eines identischen '
|
||||
'Quell-Digests ist '
|
||||
'idempotent und erzeugt '
|
||||
'keine Revision.',
|
||||
'Importierte '
|
||||
'Definitionen erfordern '
|
||||
'eine normale '
|
||||
'Überprüfung und '
|
||||
'Veröffentlichung von '
|
||||
'Formularen vor der '
|
||||
'Verwendung zur '
|
||||
'Laufzeit.']}}
|
||||
@@ -1,5 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from govoplan_core.core.modules import with_documentation_structured_translations
|
||||
from govoplan_forms.backend.german_structured_documentation import GERMAN_STRUCTURED_TRANSLATIONS
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from govoplan_core.core.access import (
|
||||
@@ -13,6 +16,7 @@ from govoplan_core.core.module_guards import (
|
||||
)
|
||||
from govoplan_core.core.modules import (
|
||||
CapabilityDocumentation,
|
||||
DocumentationCondition,
|
||||
DocumentationLink,
|
||||
DocumentationTopic,
|
||||
FrontendModule,
|
||||
@@ -47,7 +51,7 @@ from govoplan_forms.backend.semantic_subjects import (
|
||||
|
||||
MODULE_ID = "forms"
|
||||
MODULE_NAME = "Forms"
|
||||
MODULE_VERSION = "0.1.20"
|
||||
MODULE_VERSION = "0.1.23"
|
||||
READ_SCOPE = "forms:definition:read"
|
||||
WRITE_SCOPE = "forms:definition:write"
|
||||
ADMIN_SCOPE = "forms:definition:admin"
|
||||
@@ -97,7 +101,9 @@ def _semantic_subjects(
|
||||
|
||||
|
||||
def _configuration_provider(_context: ModuleContext):
|
||||
from govoplan_forms.backend.configuration_provider import SqlFormsConfigurationProvider
|
||||
from govoplan_forms.backend.configuration_provider import (
|
||||
SqlFormsConfigurationProvider,
|
||||
)
|
||||
|
||||
return SqlFormsConfigurationProvider()
|
||||
|
||||
@@ -292,6 +298,27 @@ manifest = ModuleManifest(
|
||||
kind="repository",
|
||||
),
|
||||
),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Konfigurierte Bedeutung von Formularen und Feldern dokumentieren",
|
||||
"summary": (
|
||||
"Mandanteneigene semantische Erläuterungen an ein berechtigtes Formular, Feld oder einen Abschnitt anfügen, ohne das "
|
||||
"Schema zu verändern."
|
||||
),
|
||||
"body": (
|
||||
"Ist Docs installiert, liefert Forms dokumentationssichere Subjekte für jede zugängliche aktuelle Definition sowie ihre "
|
||||
"stabilen Felder und Abschnitte. Die Subjektidentität übersteht Änderungen an Bezeichnung und Reihenfolge. Ein gelöschter "
|
||||
"und später neu angelegter Schlüssel erhält eine neue Abstammungsidentität; ältere Dokumentation bleibt ausdrücklich "
|
||||
"verwaist, statt stillschweigend neu angefügt zu werden. Fingerabdrücke ändern sich nur, wenn sich relevante Formular-, "
|
||||
"Feld-, Lokalisierungs-, Hierarchie-, Validierungs- oder Sichtbarkeitssemantik ändert, und fordern dann eine redaktionelle "
|
||||
"Prüfung an; Docs-Inhalte werden niemals automatisch veröffentlicht oder entwertet. Semantischer Text darf Bedeutung, "
|
||||
"Erhebungszweck, Interpretation, beabsichtigte und nicht beabsichtigte Verwendung sowie Beispiele erläutern, aber weder "
|
||||
"Feldtyp, Pflichtstatus, Einschränkungen, Validierung, Optionen, Richtlinie noch übermittelte Werte überstimmen. Forms prüft "
|
||||
"Mandant und Leseberechtigung für Ermittlung, direkte Auflösung, Kontexthilfe, Suche und Docs-Projektion erneut. Fehlt Docs, "
|
||||
"funktionieren Formularerstellung und statische Hilfe unverändert weiter."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"help_contexts": ["forms.semantic-documentation"],
|
||||
@@ -317,6 +344,20 @@ manifest = ModuleManifest(
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("user", "operator", "module_admin", "auditor"),
|
||||
related_modules=("core", "forms_runtime", "docs", "audit"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Betroffenenanfragen für Formulardefinitionen",
|
||||
"summary": "Aktivität der Definitionsautoren exportieren, ohne Schemata als übermittelte Werte zu behandeln.",
|
||||
"body": (
|
||||
"Forms gleicht nur eine exakte mandantenbezogene Kontokennung ab und kann eine bereits verifizierte Suche auf ein "
|
||||
"Formular oder eine Definitionsrevision begrenzen. Ausgegeben werden unveränderliche Revisionskennung, "
|
||||
"Lebenszykluszustand und Zeitpunkte der Definitionsarbeit der betroffenen Person. Titel, Suchtext, Schemanutzdaten, "
|
||||
"Feldsemantik, Richtlinienverweise und Inhalte von Änderungsgründen sind nicht enthalten. Forms speichert keine "
|
||||
"übermittelten Werte; Forms Runtime und der zuständige Service exportieren diese Datensätze getrennt. Die Zuordnung der "
|
||||
"Definitionsarbeit bleibt mit der unveränderlichen Schemahistorie erhalten."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"help_contexts": ["forms.catalogue", "privacy.data-subject-requests"],
|
||||
"consequence_classes": {
|
||||
@@ -330,6 +371,8 @@ manifest = ModuleManifest(
|
||||
title="Reusable form definitions",
|
||||
summary="Create immutable, versioned schemas consumed by Forms Runtime and institutional services.",
|
||||
body=(
|
||||
"Documentation books sit beside Form definitions and the revision dialog title. The separate "
|
||||
"Form meaning label carries the exact definition’s semantic documentation book. "
|
||||
"Each revision fixes field types, options, constraints, draft, attachment, signature, policy, and handoff requirements. "
|
||||
"Publishing is explicit; existing submissions continue to retain their exact revision. The catalogue, filter, empty states, "
|
||||
"section headings, and definition field groups use the shared responsive layout language and reflow at narrow widths without "
|
||||
@@ -338,6 +381,11 @@ manifest = ModuleManifest(
|
||||
layer="configured",
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("user", "operator", "module_admin", "product_owner"),
|
||||
conditions=(
|
||||
DocumentationCondition(
|
||||
any_scopes=(READ_SCOPE, WRITE_SCOPE, ADMIN_SCOPE)
|
||||
),
|
||||
),
|
||||
links=(
|
||||
DocumentationLink(
|
||||
label="Forms boundary and recovery",
|
||||
@@ -345,7 +393,26 @@ manifest = ModuleManifest(
|
||||
kind="repository",
|
||||
),
|
||||
),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Wiederverwendbare Formulardefinitionen erstellen",
|
||||
"summary": (
|
||||
"Unveränderliche versionierte Schemata erstellen, die Forms Runtime und institutionelle Services verwenden."
|
||||
),
|
||||
"body": (
|
||||
"Dokumentationsbücher stehen neben Formulardefinitionen und dem Titel des Revisionsdialogs. "
|
||||
"Die separate Bezeichnung Formularbedeutung trägt das Buch zur semantischen Dokumentation der "
|
||||
"genauen Definition. "
|
||||
"Jede Revision legt Feldtypen, Optionen, Einschränkungen sowie Anforderungen an Entwurf, Anlagen, Signaturen, "
|
||||
"Richtlinien und Übergaben fest. Die Veröffentlichung erfolgt ausdrücklich; vorhandene Einreichungen behalten ihre "
|
||||
"exakte Revision. Katalog, Filter, Leerzustände, Abschnittsüberschriften und Feldgruppen der Definition verwenden die "
|
||||
"gemeinsame responsive Layoutsprache und ordnen sich bei geringer Breite neu an, ohne Filterumfang, Berechtigungen oder "
|
||||
"Lebenszykluswirkungen zu verändern."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"seed": True,
|
||||
"help_contexts": [
|
||||
"forms.navigation",
|
||||
@@ -456,7 +523,7 @@ manifest = ModuleManifest(
|
||||
"workflow instance",
|
||||
),
|
||||
reference_packages=("product.service-to-decision",),
|
||||
migration_docs=("docs/FORMS_BOUNDARY.md",),
|
||||
migration_docs=("docs/FORMS_BOUNDARY.md", "docs/INTERFACE_PATTERN_MIGRATION.md"),
|
||||
recovery_docs=("docs/FORMS_BOUNDARY.md",),
|
||||
security_docs=("docs/FORMS_BOUNDARY.md",),
|
||||
operations_docs=("docs/FORMS_BOUNDARY.md",),
|
||||
@@ -464,5 +531,10 @@ manifest = ModuleManifest(
|
||||
)
|
||||
|
||||
|
||||
manifest = with_documentation_structured_translations(
|
||||
manifest, locale="de", translations=GERMAN_STRUCTURED_TRANSLATIONS
|
||||
)
|
||||
|
||||
|
||||
def get_manifest() -> ModuleManifest:
|
||||
return manifest
|
||||
|
||||
@@ -11,6 +11,14 @@ from govoplan_core.core.semantic_documentation import (
|
||||
|
||||
|
||||
class FormsInterfaceDocumentationContractTests(unittest.TestCase):
|
||||
def test_all_static_topics_have_complete_german_content(self) -> None:
|
||||
for topic in manifest.documentation:
|
||||
german = (topic.translations or {}).get("de", {})
|
||||
self.assertEqual({"title", "summary", "body"}, set(german), topic.id)
|
||||
self.assertTrue(
|
||||
all(str(value).strip() for value in german.values()), topic.id
|
||||
)
|
||||
|
||||
def test_route_and_surfaces_remain_declared(self) -> None:
|
||||
frontend = manifest.frontend
|
||||
self.assertIsNotNone(frontend)
|
||||
@@ -26,7 +34,10 @@ class FormsInterfaceDocumentationContractTests(unittest.TestCase):
|
||||
reference = topics["forms.reference.fields-and-consequences"]
|
||||
self.assertIn("forms.catalogue", guide.metadata["help_contexts"])
|
||||
self.assertGreaterEqual(len(guide.metadata["privacy_notes"]), 3)
|
||||
self.assertIn("forms.field.publication-state", reference.metadata["help_contexts"])
|
||||
self.assertEqual("workflow", guide.metadata["kind"])
|
||||
self.assertIn(
|
||||
"forms.field.publication-state", reference.metadata["help_contexts"]
|
||||
)
|
||||
self.assertIn("publish", reference.metadata["consequence_classes"])
|
||||
self.assertIn("import_package", reference.metadata["consequence_classes"])
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/forms-webui",
|
||||
"version": "0.1.20",
|
||||
"version": "0.1.23",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { FormGrid,
|
||||
ConfirmDialog,
|
||||
Dialog,
|
||||
DocumentationHelpLink,
|
||||
TextWithHelp,
|
||||
DismissibleAlert,
|
||||
FormField as Field,
|
||||
IconButton,
|
||||
@@ -193,6 +194,7 @@ export default function FormDefinitionDialog({
|
||||
<Dialog
|
||||
open={open}
|
||||
title={definition ? `Revise ${definition.title}` : "New Form definition"}
|
||||
titleHelp={<DocumentationHelpLink reference={FORMS_FIELD_DOCUMENTATION} />}
|
||||
onClose={requestClose}
|
||||
closeDisabled={busy}
|
||||
portal
|
||||
@@ -207,9 +209,8 @@ export default function FormDefinitionDialog({
|
||||
}>
|
||||
<div className="form-definition-editor">
|
||||
<div className="form-definition-help">
|
||||
<DocumentationHelpLink reference={FORMS_FIELD_DOCUMENTATION} />
|
||||
{definition && <>
|
||||
<DocumentationHelpLink reference={semanticFormDocumentation(definition)} />
|
||||
<TextWithHelp help={<DocumentationHelpLink reference={semanticFormDocumentation(definition)} />}>Form meaning</TextWithHelp>
|
||||
<a className="btn btn-secondary" href={semanticAuthoringHref(definition)} target="_blank" rel="noreferrer">
|
||||
<BookOpen size={16} aria-hidden="true" /> Document form meaning
|
||||
</a>
|
||||
|
||||
@@ -162,6 +162,8 @@ export default function FormsPage({ settings, auth }: PlatformRouteContext) {
|
||||
<main className="forms-page">
|
||||
<WorkspaceFrame className="forms-shell" label="Form definitions" interfaceId="forms.catalogue" helpContextId="forms.page.catalogue" helpModuleId="forms">
|
||||
<WorkspaceActionBar
|
||||
title="Form definitions"
|
||||
titleHelp={<DocumentationHelpLink reference={FORMS_DOCUMENTATION} />}
|
||||
scope="workspace"
|
||||
variant="collection"
|
||||
refreshable
|
||||
@@ -187,7 +189,6 @@ export default function FormsPage({ settings, auth }: PlatformRouteContext) {
|
||||
<span className="forms-count">{total}</span>
|
||||
</>}
|
||||
createAction={<Button variant="primary" disabled={!canWrite} disabledReason={!canWrite ? FORMS_I18N.writeReason : undefined} onClick={() => setEditing("new")}><Plus size={16} aria-hidden="true" />New definition</Button>}
|
||||
helpAction={<DocumentationHelpLink reference={FORMS_DOCUMENTATION} />}
|
||||
/>
|
||||
<PageScrollViewport className="forms-list-viewport">
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { PlatformTranslations } from "@govoplan/core-webui";
|
||||
|
||||
const en = {
|
||||
"Form meaning": "Form meaning",
|
||||
"i18n:govoplan-forms.form_definitions": "Form definitions",
|
||||
"i18n:govoplan-forms.loading_reason": "Form definitions are still loading.",
|
||||
"i18n:govoplan-forms.busy_reason": "Another Form definition action is still running.",
|
||||
@@ -74,6 +75,7 @@ const en = {
|
||||
} as const;
|
||||
|
||||
const de: Record<keyof typeof en, string> = {
|
||||
"Form meaning": "Formularbedeutung",
|
||||
"i18n:govoplan-forms.form_definitions": "Formulardefinitionen",
|
||||
"i18n:govoplan-forms.loading_reason": "Formulardefinitionen werden noch geladen.",
|
||||
"i18n:govoplan-forms.busy_reason": "Eine andere Aktion für Formulardefinitionen läuft noch.",
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ const FormsPage = lazy(() => import("./features/forms/FormsPage"));
|
||||
export const formsModule: PlatformWebModule = {
|
||||
id: "forms",
|
||||
label: "i18n:govoplan-forms.form_definitions",
|
||||
version: "0.1.20",
|
||||
version: "0.1.21",
|
||||
dependencies: ["access"],
|
||||
optionalDependencies: ["forms_runtime", "portal", "workflow_engine", "cases", "policy", "docs"],
|
||||
translations: generatedTranslations,
|
||||
|
||||
Reference in New Issue
Block a user