5 Commits
Author SHA1 Message Date
zemion ee7dbd99b8 fix(ui): align contextual documentation with headings
Verified with the coordinated workspace changes by devkit full run
2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed).
This shared UI pass does not mark the individual module reviews complete.
2026-09-09 02:04:14 +02:00
zemion c2a3c24f82 fix(packaging): expose immutable WebUI Git package for v0.1.23
Module Package Release / publish-packages (push) Successful in 11s
2026-09-08 02:06:11 +02:00
zemion bb6ebf194a Release govoplan-tasks v0.1.23: unify interface contracts and documentation 2026-09-08 01:32:53 +02:00
zemion 5c186b565e feat: promote work to a stable product destination
Module Package Release / publish-packages (push) Successful in 12s
2026-08-24 17:58:22 +02:00
zemion 39e9c6c2ea docs: complete German structured documentation
Module Package Release / publish-packages (push) Successful in 12s
2026-08-24 01:15:41 +02:00
10 changed files with 172 additions and 34 deletions
+18
View File
@@ -10,3 +10,21 @@ Notifications keeps delivery state, and each domain module keeps its business
objects and commands. objects and commands.
See [Tasks Domain And Unified Work Inbox](docs/TASKS_DOMAIN.md). See [Tasks Domain And Unified Work Inbox](docs/TASKS_DOMAIN.md).
## Git-source WebUI package
The repository root exposes `@govoplan/tasks-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/tasks-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.
+29 -3
View File
@@ -1,8 +1,34 @@
{ {
"name": "@govoplan/tasks", "name": "@govoplan/tasks-webui",
"version": "0.1.20", "version": "0.1.23",
"private": true, "private": true,
"description": "Governed work items and unified work inbox for GovOPlaN.", "description": "Governed work items and unified work inbox for GovOPlaN.",
"type": "module", "type": "module",
"peerDependencies": {} "peerDependencies": {
"@govoplan/core-webui": "^0.1.45",
"lucide-react": "^1.23.0",
"react": ">=19.2.7 <20",
"react-dom": ">=19.2.7 <20",
"react-router": ">=8.3.0 <9"
},
"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/tasks.css": "./webui/src/styles/tasks.css"
},
"peerDependenciesMeta": {
"@govoplan/core-webui": {
"optional": true
}
},
"files": [
"webui/src",
"README.md",
"LICENSE"
]
} }
+2 -2
View File
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "govoplan-tasks" name = "govoplan-tasks"
version = "0.1.20" version = "0.1.23"
description = "Governed work items and unified work inbox for GovOPlaN." description = "Governed work items and unified work inbox for GovOPlaN."
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
authors = [{ name = "GovOPlaN" }] authors = [{ name = "GovOPlaN" }]
dependencies = [ dependencies = [
"govoplan-core>=0.1.18", "govoplan-core>=0.1.45",
"govoplan-access>=0.1.18", "govoplan-access>=0.1.18",
] ]
+1 -1
View File
@@ -1,3 +1,3 @@
"""GovOPlaN Tasks module.""" """GovOPlaN Tasks module."""
__version__ = "0.1.20" __version__ = "0.1.23"
@@ -0,0 +1,29 @@
"""German translations for public structured documentation metadata."""
from __future__ import annotations
from typing import Any
GERMAN_STRUCTURED_TRANSLATIONS: dict[str, dict[str, Any]] = {'tasks.data-subject-requests': {'consequence_classes': {'export_assigned_task': 'Gibt nur '
'eingeschränkte '
'aufgabeneigene '
'Daten und genaue '
'Kontozuweisung '
'zurück.',
'retain_actor_attribution': 'Bewahrt '
'minimierte '
'Task-Lifecycle-Rechenschaftsnachweise '
'vor.',
'review_assignment_erasure': 'Benötigt '
'den '
'Besitzer '
'der '
'Task-Quelle '
'und die '
'Aufbewahrungsberechtigung, '
'bevor Sie '
'die '
'freigegebene '
'Arbeit '
'ändern.'}}}
+59 -3
View File
@@ -1,5 +1,8 @@
from __future__ import annotations from __future__ import annotations
from govoplan_core.core.modules import with_documentation_structured_translations
from govoplan_tasks.backend.german_structured_documentation import GERMAN_STRUCTURED_TRANSLATIONS
from pathlib import Path from pathlib import Path
from govoplan_core.core.access import ( from govoplan_core.core.access import (
@@ -23,7 +26,9 @@ from govoplan_core.core.modules import (
ModuleManifest, ModuleManifest,
NavItem, NavItem,
PermissionDefinition, PermissionDefinition,
ProductAvailabilityExplanation,
ProductAreaContribution, ProductAreaContribution,
ProductSurfaceContribution,
QuickAccessTool, QuickAccessTool,
RoleTemplate, RoleTemplate,
) )
@@ -44,7 +49,7 @@ from govoplan_tasks.backend.service import SqlTaskService
MODULE_ID = "tasks" MODULE_ID = "tasks"
MODULE_NAME = "Tasks" MODULE_NAME = "Tasks"
MODULE_VERSION = "0.1.20" MODULE_VERSION = "0.1.23"
READ_SCOPE = "tasks:item:read" READ_SCOPE = "tasks:item:read"
WRITE_SCOPE = "tasks:item:write" WRITE_SCOPE = "tasks:item:write"
ADMIN_SCOPE = "tasks:item:admin" ADMIN_SCOPE = "tasks:item:admin"
@@ -131,6 +136,26 @@ ROLE_TEMPLATES = (
) )
DOCUMENTATION = ( DOCUMENTATION = (
DocumentationTopic(
id="tasks.workspace-layout",
title="Tasks workspace actions",
summary="Find collection-wide commands in their consistent workspace position.",
body="Documentation books sit immediately beside the visible heading or contextual label for "
"Tasks, not among operational action buttons. Field help remains beside its label. "
"Reload and Create task use the persistent full-width workspace header at the upper right; Reload sits immediately before creation. Selecting a record, changing filters, or opening an editor does not move these collection-wide commands into the left pane. Search and status filters stay in the task list; completion, ownership, and other task-specific actions remain with the selected task. Existing permissions, disabled-state rules, and unsaved-change guards still apply. Administrators configure authority through the existing permission system; no new permission or automatic operation is introduced.",
layer="static",
documentation_types=("user", "admin"),
audience=("user", "module_admin", "operator"),
order=5,
translations={"de": {
"title": "Aufgaben: Aktionen im Arbeitsbereich",
"summary": "Sammlungsweite Aktionen an ihrer einheitlichen Position im Arbeitsbereich finden.",
"body": "Dokumentationsbücher stehen unmittelbar neben der sichtbaren Überschrift oder "
"Kontextbezeichnung für Aufgaben, nicht zwischen ausführbaren Aktionsschaltflächen. Feldhilfe "
"bleibt neben der Feldbezeichnung. "
"Neu laden und Aufgabe anlegen stehen oben rechts in der dauerhaft sichtbaren, arbeitsbereichsweiten Leiste; Neu laden steht unmittelbar vor dem Anlegen. Auswahl, Filterwechsel und Bearbeitung verschieben diese sammlungsweiten Aktionen nicht in den linken Bereich. Suche und Statusfilter bleiben in der Aufgabenliste; Abschluss, Zuständigkeit und weitere aufgabenspezifische Aktionen bleiben bei der ausgewählten Aufgabe. Bestehende Berechtigungen, Deaktivierungsregeln und der Schutz ungespeicherter Änderungen gelten weiterhin. Administratoren konfigurieren Rechte im bestehenden Berechtigungssystem; es entstehen weder neue Rechte noch automatische Vorgänge.",
}},
),
DocumentationTopic( DocumentationTopic(
id="tasks.data-subject-requests", id="tasks.data-subject-requests",
title="Task data-subject requests", title="Task data-subject requests",
@@ -206,7 +231,8 @@ DOCUMENTATION = (
title="Work in product navigation and Quick Access", title="Work in product navigation and Quick Access",
summary="Keep assigned work available in the Work area and the optional right-side Quick Access rail.", summary="Keep assigned work available in the Work area and the optional right-side Quick Access rail.",
body=( body=(
"Tasks contributes its authorized workspace to the Work product area. When Quick Access is enabled, " "Tasks contributes its authorized workspace to the stable Work destination at /work. The owner route "
"/tasks remains available through All available tools and as a compatible deep link. When Quick Access is enabled, "
"a bounded seven-item authorized inbox and detail can appear beside the current page. Explicit Tasks can be " "a bounded seven-item authorized inbox and detail can appear beside the current page. Explicit Tasks can be "
"started or completed there; work from another provider exposes only that provider's launch path. Every load " "started or completed there; work from another provider exposes only that provider's launch path. Every load "
"and command is rechecked by Tasks, and completion returns a typed work-item reference to the host. Views may " "and command is rechecked by Tasks, and completion returns a typed work-item reference to the host. Views may "
@@ -221,7 +247,8 @@ DOCUMENTATION = (
"title": "Arbeit in Produktnavigation und Schnellzugriff", "title": "Arbeit in Produktnavigation und Schnellzugriff",
"summary": "Zugewiesene Arbeit im Produktbereich Arbeit und optional in der rechten Schnellzugriffsleiste verwenden.", "summary": "Zugewiesene Arbeit im Produktbereich Arbeit und optional in der rechten Schnellzugriffsleiste verwenden.",
"body": ( "body": (
"Tasks ordnet den berechtigten Arbeitsbereich dem Produktbereich Arbeit zu. Ist der Schnellzugriff aktiviert, " "Tasks ordnet den berechtigten Arbeitsbereich dem stabilen Produktziel Arbeit unter /work zu. Der Eigentümerpfad "
"/tasks bleibt unter Alle verfügbaren Werkzeuge und als kompatibler Direktlink erreichbar. Ist der Schnellzugriff aktiviert, "
"kann ein begrenzter, berechtigungsgeprüfter Arbeitsvorrat mit sieben Einträgen und Details neben der " "kann ein begrenzter, berechtigungsgeprüfter Arbeitsvorrat mit sieben Einträgen und Details neben der "
"aktuellen Seite erscheinen. Explizite Tasks lassen sich dort beginnen oder abschließen; fremde Quellen " "aktuellen Seite erscheinen. Explizite Tasks lassen sich dort beginnen oder abschließen; fremde Quellen "
"behalten ihre eigenen Befehle und Startpfade. Jeder Aufruf wird erneut durch Tasks geprüft. Ansichten " "behalten ihre eigenen Befehle und Startpfade. Jeder Aufruf wird erneut durch Tasks geprüft. Ansichten "
@@ -404,6 +431,30 @@ manifest = ModuleManifest(
order=10, order=10,
), ),
), ),
product_surfaces=(
ProductSurfaceContribution(
id="work.items",
module_id=MODULE_ID,
label="i18n:govoplan-core.product_surface.work",
description="i18n:govoplan-core.product_surface.work_description",
icon="list-checks",
entry_path="/work",
route_path="/tasks",
surface_ids=("tasks.route.work",),
presentations=("task", "reader"),
help_context_ids=("tasks.route.work",),
documentation_topic_ids=("tasks.quick-access-and-product-area",),
required_any=(READ_SCOPE,),
order=10,
unavailable=ProductAvailabilityExplanation(
reason="authorization",
title="i18n:govoplan-core.product_surface.unavailable",
description="i18n:govoplan-core.product_surface.unavailable_description",
resolution="i18n:govoplan-core.product_surface.unavailable_resolution",
responsible_role="i18n:govoplan-core.access_administrator",
),
),
),
quick_access_tools=( quick_access_tools=(
QuickAccessTool( QuickAccessTool(
id="tasks.work", id="tasks.work",
@@ -492,6 +543,11 @@ manifest = ModuleManifest(
) )
manifest = with_documentation_structured_translations(
manifest, locale="de", translations=GERMAN_STRUCTURED_TRANSLATIONS
)
def get_manifest() -> ModuleManifest: def get_manifest() -> ModuleManifest:
return manifest return manifest
+6 -1
View File
@@ -16,7 +16,12 @@ class TasksQuickAccessContractTests(unittest.TestCase):
topics = get_manifest().documentation topics = get_manifest().documentation
kinds = {topic.metadata.get("kind", "system") for topic in topics} kinds = {topic.metadata.get("kind", "system") for topic in topics}
self.assertEqual(3, len(topics)) self.assertEqual({
"tasks.workspace-layout",
"tasks.data-subject-requests",
"tasks.quick-access-and-product-area",
"tasks.work-inbox",
}, {topic.id for topic in topics})
self.assertTrue({"workflow", "reference"}.issubset(kinds)) self.assertTrue({"workflow", "reference"}.issubset(kinds))
for topic in topics: for topic in topics:
translation = topic.translations["de"] translation = topic.translations["de"]
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@govoplan/tasks-webui", "name": "@govoplan/tasks-webui",
"version": "0.1.20", "version": "0.1.23",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
@@ -14,7 +14,7 @@
"./styles/tasks.css": "./src/styles/tasks.css" "./styles/tasks.css": "./src/styles/tasks.css"
}, },
"peerDependencies": { "peerDependencies": {
"@govoplan/core-webui": "^0.1.18", "@govoplan/core-webui": "^0.1.45",
"lucide-react": "^1.23.0", "lucide-react": "^1.23.0",
"react": ">=19.2.7 <20", "react": ">=19.2.7 <20",
"react-dom": ">=19.2.7 <20", "react-dom": ">=19.2.7 <20",
+19 -20
View File
@@ -124,6 +124,23 @@ export default function TasksPage({ settings, auth }: { settings: ApiSettings; a
return ( return (
<WorkspaceFrame as="main" height="viewport" surface="plain" className="tasks-page" label="Task inbox" data-help-context-id="tasks.page.inbox"> <WorkspaceFrame as="main" height="viewport" surface="plain" className="tasks-page" label="Task inbox" data-help-context-id="tasks.page.inbox">
<WorkspaceActionBar
scope="workspace"
variant="collection"
refreshable
reloadAction={{ onReload: () => void load(), loading: loading || busy, label: "i18n:govoplan-tasks.refresh" }}
contextActions={<span className="tasks-title"><ListChecks size={18} /><strong>i18n:govoplan-tasks.work</strong></span>}
createAction={canWrite ? (
<Button
variant="primary"
onClick={() => setCreateOpen(true)}
disabled={busy}
helpContextId="tasks.action.create"
>
<Plus size={16} aria-hidden="true" /> i18n:govoplan-tasks.create_task
</Button>
) : undefined}
/>
<WorkspaceLayout <WorkspaceLayout
variant="split" variant="split"
primarySize="default" primarySize="default"
@@ -138,24 +155,6 @@ export default function TasksPage({ settings, auth }: { settings: ApiSettings; a
helpContextId="tasks.page.inbox" helpContextId="tasks.page.inbox"
helpModuleId="tasks" helpModuleId="tasks"
primary={<> primary={<>
<WorkspaceActionBar
scope="collection-pane"
variant="collection"
refreshable
reloadAction={{ onReload: () => void load(), loading: loading || busy, label: "i18n:govoplan-tasks.refresh" }}
className="tasks-sidebar-bar"
contextActions={<span className="tasks-title"><ListChecks size={18} /><strong>i18n:govoplan-tasks.work</strong></span>}
createAction={canWrite ? (
<Button
variant="primary"
onClick={() => setCreateOpen(true)}
disabled={busy}
helpContextId="tasks.action.create"
>
<Plus size={16} aria-hidden="true" /> i18n:govoplan-tasks.create_task
</Button>
) : undefined}
/>
<FilterBar as="form" surface="control" wrap="never" className="tasks-search" onSubmit={submitSearch} role="search"> <FilterBar as="form" surface="control" wrap="never" className="tasks-search" onSubmit={submitSearch} role="search">
<Search size={15} aria-hidden="true" /> <Search size={15} aria-hidden="true" />
<input <input
@@ -204,8 +203,8 @@ export default function TasksPage({ settings, auth }: { settings: ApiSettings; a
variant="detail" variant="detail"
className="tasks-topbar" className="tasks-topbar"
data-help-context-id="tasks.page.detail" data-help-context-id="tasks.page.detail"
contextActions={<span className="tasks-detail-title"><ListChecks size={18} /><strong>{selected?.title ?? "i18n:govoplan-tasks.work_details"}</strong></span>} title={<span className="tasks-detail-title"><ListChecks size={18} aria-hidden="true" /><strong>{selected?.title ?? "i18n:govoplan-tasks.work_details"}</strong></span>}
helpAction={<DocumentationHelpLink reference={DOCUMENTATION} />} titleHelp={<DocumentationHelpLink reference={DOCUMENTATION} />}
primaryActions={selected?.provider_id === "tasks.explicit" && canWrite ? ( primaryActions={selected?.provider_id === "tasks.explicit" && canWrite ? (
<TaskPrimaryActions item={selected} busy={busy} onAction={(action) => action === "defer" ? setDeferOpen(true) : void runAction(action)} /> <TaskPrimaryActions item={selected} busy={busy} onAction={(action) => action === "defer" ? setDeferOpen(true) : void runAction(action)} />
) : undefined} ) : undefined}
+7 -2
View File
@@ -4,6 +4,7 @@ import type {
PlatformWebModule, PlatformWebModule,
QuickAccessToolsUiCapability QuickAccessToolsUiCapability
} from "@govoplan/core-webui"; } from "@govoplan/core-webui";
import { generatedTranslations as productSurfaceTranslations } from "@govoplan/core-webui/outcome-product-surface-translations";
import TasksSummaryWidget from "./features/tasks/TasksSummaryWidget"; import TasksSummaryWidget from "./features/tasks/TasksSummaryWidget";
import TasksQuickAccess from "./features/tasks/TasksQuickAccess"; import TasksQuickAccess from "./features/tasks/TasksQuickAccess";
import { generatedTranslations } from "./i18n/generatedTranslations"; import { generatedTranslations } from "./i18n/generatedTranslations";
@@ -11,6 +12,10 @@ import "./styles/tasks.css";
const TasksPage = lazy(() => import("./features/tasks/TasksPage")); const TasksPage = lazy(() => import("./features/tasks/TasksPage"));
const readScope = ["tasks:item:read"]; const readScope = ["tasks:item:read"];
const translations = {
en: { ...generatedTranslations.en, ...productSurfaceTranslations.en },
de: { ...generatedTranslations.de, ...productSurfaceTranslations.de }
};
const dashboardWidgets: DashboardWidgetsUiCapability = { const dashboardWidgets: DashboardWidgetsUiCapability = {
widgets: [ widgets: [
@@ -44,10 +49,10 @@ const quickAccessTools: QuickAccessToolsUiCapability = {
export const tasksModule: PlatformWebModule = { export const tasksModule: PlatformWebModule = {
id: "tasks", id: "tasks",
label: "i18n:govoplan-tasks.work", label: "i18n:govoplan-tasks.work",
version: "0.1.20", version: "0.1.22",
dependencies: ["access"], dependencies: ["access"],
optionalDependencies: ["idm", "organizations", "workflow_engine", "workflow", "notifications", "postbox", "approvals", "views", "dashboard", "search"], optionalDependencies: ["idm", "organizations", "workflow_engine", "workflow", "notifications", "postbox", "approvals", "views", "dashboard", "search"],
translations: generatedTranslations, translations,
navItems: [ navItems: [
{ {
to: "/tasks", to: "/tasks",