Migrate Views interface patterns

This commit is contained in:
2026-08-03 11:57:44 +02:00
parent c433434570
commit c125f332ba
12 changed files with 1013 additions and 198 deletions
+4
View File
@@ -47,6 +47,10 @@ without a reachable navigation entry and route, Core shows the normal
authorized interface and reports the stale projection instead of trapping the authorized interface and reports the stale projection instead of trapping the
user. user.
The surface inventory, consequence classes, availability explanations, and
accessibility evidence for the shared interface language are recorded in
[`docs/INTERFACE_PATTERN_MIGRATION.md`](docs/INTERFACE_PATTERN_MIGRATION.md).
## Development ## Development
Install the module as an editable package alongside Core and include `views` in Install the module as an editable package alongside Core and include `views` in
+48
View File
@@ -0,0 +1,48 @@
# Views Interface Pattern Migration
This document records the bounded migration of Views-owned WebUI surfaces to
the GovOPlaN interface pattern language. Core owns shared controls, surface
projection, and the host shells. Views owns definitions, revisions,
assignments, effective selection, and the safeguards described here.
## Surface Inventory
| Surface | Archetype | Consequence class | Contract |
| --- | --- | --- | --- |
| `views.selector` | Compact global selector | Change active presentation projection | Localized accessible selector, required-state explanation, unsaved-change guard |
| `views.admin.system` | Administration list-detail editor | Create, publish, archive, assign | Shared admin layout, explicit disabled reasons, required-View lockout validation, contextual help |
| `views.admin.tenant` | Administration list-detail editor | Create, publish, archive, assign | Inheritance provenance, searchable group/user targets, explicit optional-module blocker |
| `views.settings.personal` | Settings list-detail editor | Create and publish owned Views | Shared owner selector, permission blocker, contextual field help |
## Consequence And Availability Rules
- Views filter presentation only. Authorization remains owned by Access and
each protected backend route.
- Published revisions are immutable. Unpinned assignments follow later
publications; pinned assignments retain one published revision.
- Required Views keep the selector and the applicable administration escape
surface. The backend validates this before accepting the assignment.
- User and group assignments take precedence over tenant and system
assignments. Inherited definitions and assignments are changed at their
owning scope, not copied implicitly.
- The optional Access directory supplies searchable group and user targets.
If it is absent, the UI explains the missing capability and its remedy while
system and tenant targets remain usable.
- Definition, creation, and assignment drafts use Core's shared unsaved-change
guard. Archive and assignment removal use shared consequential-action
confirmation.
## State And Accessibility Evidence
The surfaces use Core loading, error, success, empty, disabled-action, blocker,
dialog, status, selector, and tree controls. Consequential actions stay visible
and explain missing permission, inherited ownership, lifecycle state, missing
catalogue data, or lockout validation. Labels and accessible properties are
provided through the module translation catalogue. Shared dialogs own focus
containment and restoration, while fixed-height panes retain responsive
scrolling.
Stable help references are contributed through the manifest for the selector,
administration sections, settings panel, fields, assignment precedence, and
consequential actions. Backend and WebUI contract tests prevent those
references and safeguards from silently regressing.
+77
View File
@@ -11,6 +11,7 @@ from govoplan_core.core.module_guards import (
persistent_table_uninstall_guard, persistent_table_uninstall_guard,
) )
from govoplan_core.core.modules import ( from govoplan_core.core.modules import (
DocumentationLink,
DocumentationTopic, DocumentationTopic,
FrontendModule, FrontendModule,
MigrationSpec, MigrationSpec,
@@ -309,8 +310,84 @@ manifest = ModuleManifest(
documentation_types=("admin", "user"), documentation_types=("admin", "user"),
audience=("administrator", "power_user", "workflow_designer"), audience=("administrator", "power_user", "workflow_designer"),
related_modules=("access", "admin", "policy", "workflow_engine"), related_modules=("access", "admin", "policy", "workflow_engine"),
links=(
DocumentationLink(
label="Views administration",
href="/admin?section=system-views",
kind="runtime",
),
DocumentationLink(
label="Views API",
href="/api/v1/views/definitions",
kind="api",
),
),
metadata={
"kind": "guide",
"help_contexts": [
"views.selector",
"views.admin.system",
"views.admin.tenant",
"views.settings.personal",
"views.admin.blocked",
],
},
order=18, order=18,
), ),
DocumentationTopic(
id="views.reference.fields-and-consequences",
title="View fields, assignments, and consequences",
summary=(
"Understand immutable revisions, assignment precedence, required "
"View safeguards, and the difference between visibility and access."
),
body=(
"A View definition owns immutable revisions of visible surface IDs. "
"Publishing makes the latest revision assignable. Available assignments "
"let users opt in, defaults apply until changed, and required assignments "
"cannot be left. User and group assignments take precedence over tenant "
"and system assignments. Pinning preserves one published revision; an "
"unpinned assignment follows later publications. Required Views must keep "
"the selector and administration escape surfaces. Hiding a surface never "
"grants or revokes authorization, and inherited definitions or assignments "
"must be changed in their owning scope."
),
documentation_types=("admin",),
audience=("administrator", "power_user", "workflow_designer"),
related_modules=("access", "admin", "policy", "workflow_engine"),
links=(
DocumentationLink(
label="Views administration",
href="/admin?section=system-views",
kind="runtime",
),
DocumentationLink(
label="View assignments API",
href="/api/v1/views/assignments",
kind="api",
),
),
metadata={
"kind": "reference",
"help_contexts": [
"views.field.name",
"views.field.description",
"views.field.surfaces",
"views.field.assignment-target",
"views.field.assignment-mode",
"views.field.assignment-priority",
"views.action.publish",
"views.action.archive",
],
"consequence_classes": {
"publish": "Creates the assignable immutable revision used by unpinned assignments.",
"required": "Constrains affected users while retaining selector and administration escape surfaces.",
"archive": "Deactivates optional assignments; required assignments must be removed first.",
"remove_assignment": "Stops the target from inheriting this assignment without deleting the View.",
},
},
order=19,
),
), ),
architecture=declared_module_architecture( architecture=declared_module_architecture(
layer="governance_accountability", layer="governance_accountability",
@@ -0,0 +1,42 @@
from __future__ import annotations
import unittest
from govoplan_views.backend.manifest import manifest
class ViewsInterfaceDocumentationContractTests(unittest.TestCase):
def test_views_surfaces_remain_declared(self) -> None:
frontend = manifest.frontend
self.assertIsNotNone(frontend)
surfaces = {surface.id for surface in frontend.view_surfaces} # type: ignore[union-attr]
self.assertEqual(
{
"views.selector",
"views.admin.system",
"views.admin.tenant",
"views.settings.personal",
},
surfaces,
)
def test_views_topics_publish_stable_help_contexts(self) -> None:
topics = {topic.id: topic for topic in manifest.documentation}
self.assertIn("views.interface-projections", topics)
self.assertIn("views.reference.fields-and-consequences", topics)
projection_contexts = set(
topics["views.interface-projections"].metadata["help_contexts"]
)
self.assertIn("views.selector", projection_contexts)
self.assertIn("views.admin.system", projection_contexts)
self.assertIn("views.admin.tenant", projection_contexts)
self.assertIn("views.settings.personal", projection_contexts)
reference = topics["views.reference.fields-and-consequences"]
self.assertIn("views.action.publish", reference.metadata["help_contexts"])
self.assertIn("required", reference.metadata["consequence_classes"])
if __name__ == "__main__":
unittest.main()
+3
View File
@@ -6,6 +6,9 @@
"main": "src/index.ts", "main": "src/index.ts",
"module": "src/index.ts", "module": "src/index.ts",
"types": "src/index.ts", "types": "src/index.ts",
"scripts": {
"test:interface-patterns": "node scripts/test-interface-pattern-language.mjs"
},
"exports": { "exports": {
".": { ".": {
"types": "./src/index.ts", "types": "./src/index.ts",
@@ -0,0 +1,29 @@
import { readFileSync } from "node:fs";
function source(path) {
return readFileSync(new URL(path, import.meta.url), "utf8");
}
function assert(condition, message) {
if (!condition) throw new Error(message);
}
const admin = source("../src/features/views/ViewsAdminPanel.tsx");
const personal = source("../src/features/views/PersonalViewsPanel.tsx");
const selector = source("../src/components/ViewSelector.tsx");
const patterns = source("../src/features/views/interfacePatterns.ts");
const moduleSource = source("../src/module.ts");
const translations = source("../src/i18n/generatedTranslations.ts");
assert(admin.includes("DocumentationHelpLink") && personal.includes("VIEWS_FIELD_DOCUMENTATION"), "Views administration and personal settings expose contextual documentation");
assert(admin.includes("ActionBlockerHint") && personal.includes("ActionBlockerHint"), "Read-only and optional-capability states identify the required remedy");
assert(admin.includes("disabledReason"), "Unavailable Views actions explain their state");
assert(admin.includes("createDirty ? requestDiscard(closeCreate)") && admin.includes("assignmentDirty ? requestDiscard(closeAssignment)"), "Definition and assignment dialogs use the shared unsaved-change guard");
assert(admin.includes("ConfirmDialog") && admin.includes("archiveTarget") && admin.includes("deleteAssignmentTarget"), "Archive and assignment removal remain explicitly confirmed");
assert(admin.includes("LOCKOUT_SURFACES") && admin.includes("requiredMissing"), "Required Views retain the selector and administration escape paths");
assert(patterns.includes('topicId: "views.interface-projections"') && patterns.includes('topicId: "views.reference.fields-and-consequences"'), "Views uses stable manifest-backed help references");
assert(moduleSource.includes("generatedTranslations") && moduleSource.includes('label: "i18n:govoplan-views.views"'), "Module and surface labels are localized");
assert(selector.includes("usePlatformLanguage") && selector.includes("required_by_administrator"), "The global selector localizes and explains its locked state");
assert(translations.includes('"i18n:govoplan-views.views_administration_is_read_only"'), "Availability explanations are present in the translation catalogue");
console.log("Views surfaces satisfy the recorded interface pattern-language contract.");
+18 -8
View File
@@ -2,6 +2,7 @@ import { Eye, LockKeyhole } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { import {
dispatchPlatformViewChanged, dispatchPlatformViewChanged,
usePlatformLanguage,
useUnsavedChanges, useUnsavedChanges,
type ViewSelectorProps type ViewSelectorProps
} from "@govoplan/core-webui"; } from "@govoplan/core-webui";
@@ -14,6 +15,7 @@ export default function ViewSelector({
}: ViewSelectorProps) { }: ViewSelectorProps) {
const [busy, setBusy] = useState(false); const [busy, setBusy] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
const { translateText } = usePlatformLanguage();
const { requestNavigation } = useUnsavedChanges(); const { requestNavigation } = useUnsavedChanges();
const options = projection?.availableViews ?? []; const options = projection?.availableViews ?? [];
@@ -26,7 +28,11 @@ export default function ViewSelector({
await selectEffectiveView(settings, viewId || null); await selectEffectiveView(settings, viewId || null);
dispatchPlatformViewChanged(); dispatchPlatformViewChanged();
} catch (caught) { } catch (caught) {
setError(caught instanceof Error ? caught.message : "View selection failed"); setError(
caught instanceof Error
? caught.message
: "i18n:govoplan-views.view_selection_failed"
);
} finally { } finally {
setBusy(false); setBusy(false);
} }
@@ -41,26 +47,30 @@ export default function ViewSelector({
const locked = Boolean(projection?.locked); const locked = Boolean(projection?.locked);
const diagnostic = projection?.diagnostics.find((item) => item.severity === "error") const diagnostic = projection?.diagnostics.find((item) => item.severity === "error")
?? projection?.diagnostics[0]; ?? projection?.diagnostics[0];
const title = error || diagnostic?.message || ( const title = translateText(error || diagnostic?.message || (
locked locked
? "This View is required by an administrator." ? "i18n:govoplan-views.required_by_administrator"
: "Choose which parts of GovOPlaN are shown." : "i18n:govoplan-views.selector_help"
); ));
return ( return (
<label className="titlebar-view-selector" title={title}> <label className="titlebar-view-selector" title={title}>
{locked {locked
? <LockKeyhole size={16} aria-hidden="true" /> ? <LockKeyhole size={16} aria-hidden="true" />
: <Eye size={16} aria-hidden="true" />} : <Eye size={16} aria-hidden="true" />}
<span className="sr-only">Current View</span> <span className="sr-only">i18n:govoplan-views.current_view</span>
<select <select
value={projection?.activeViewId ?? ""} value={projection?.activeViewId ?? ""}
disabled={busy || locked} disabled={busy || locked}
aria-label="Current View" aria-label={translateText("i18n:govoplan-views.current_view")}
aria-invalid={Boolean(error) || undefined} aria-invalid={Boolean(error) || undefined}
onChange={(event) => select(event.target.value)} onChange={(event) => select(event.target.value)}
> >
{!locked && <option value="">Full interface</option>} {!locked && (
<option value="">
{translateText("i18n:govoplan-views.full_interface")}
</option>
)}
{options.map((option) => ( {options.map((option) => (
<option key={option.id} value={option.id}>{option.name}</option> <option key={option.id} value={option.id}>{option.name}</option>
))} ))}
+31 -10
View File
@@ -1,12 +1,19 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { import {
ActionBlockerHint,
FormField, FormField,
hasScope, hasScope,
i18nMessage,
type ApiSettings, type ApiSettings,
type AuthInfo type AuthInfo
} from "@govoplan/core-webui"; } from "@govoplan/core-webui";
import ViewsAdminPanel from "./ViewsAdminPanel"; import ViewsAdminPanel from "./ViewsAdminPanel";
import type { ViewScopeType } from "../../api/views"; import type { ViewScopeType } from "../../api/views";
import {
VIEWS_DOCUMENTATION,
VIEWS_FIELD_DOCUMENTATION,
VIEWS_INTERFACE_I18N
} from "./interfacePatterns";
type OwnerOption = { type OwnerOption = {
@@ -38,9 +45,20 @@ export default function PersonalViewsPanel({
const owner = options.find((option) => option.key === ownerKey) ?? options[0]; const owner = options.find((option) => option.key === ownerKey) ?? options[0];
if (!owner) { if (!owner) {
return ( return (
<p className="muted"> <ActionBlockerHint
You do not have access to personal or group View definitions. reason={{
</p> summary: "i18n:govoplan-views.no_personal_access",
requiredAction: VIEWS_INTERFACE_I18N.permissionGuidanceText,
actor: VIEWS_INTERFACE_I18N.administratorText,
target: VIEWS_INTERFACE_I18N.administrationTargetText
}}
labels={{
requiredAction: VIEWS_INTERFACE_I18N.requiredActionText,
actor: VIEWS_INTERFACE_I18N.actorText,
target: VIEWS_INTERFACE_I18N.destinationText
}}
documentation={VIEWS_DOCUMENTATION}
/>
); );
} }
@@ -48,7 +66,10 @@ export default function PersonalViewsPanel({
<div className="views-personal-panel"> <div className="views-personal-panel">
{options.length > 1 && ( {options.length > 1 && (
<div className="views-owner-toolbar"> <div className="views-owner-toolbar">
<FormField label="View owner"> <FormField
label="i18n:govoplan-views.view_owner"
documentation={VIEWS_FIELD_DOCUMENTATION}
>
<select <select
value={owner.key} value={owner.key}
onChange={(event) => setOwnerKey(event.target.value)} onChange={(event) => setOwnerKey(event.target.value)}
@@ -91,9 +112,8 @@ function ownerOptions(auth: AuthInfo): OwnerOption[] {
key: `user:${auth.user.account_id}`, key: `user:${auth.user.account_id}`,
scopeType: "user", scopeType: "user",
scopeId: auth.user.account_id, scopeId: auth.user.account_id,
label: "My Views", label: "i18n:govoplan-views.my_views",
description: description: "i18n:govoplan-views.personal_description",
"Design personal interface projections. Publishing a View makes it available to this account.",
canWrite: canWrite:
tenantManager || tenantManager ||
hasScope(auth, "views:personal_definition:write") hasScope(auth, "views:personal_definition:write")
@@ -109,9 +129,10 @@ function ownerOptions(auth: AuthInfo): OwnerOption[] {
key: `group:${group.id}`, key: `group:${group.id}`,
scopeType: "group", scopeType: "group",
scopeId: group.id, scopeId: group.id,
label: `Group: ${group.name}`, label: i18nMessage("i18n:govoplan-views.group_value", {
description: value0: group.name
"Design reusable interface projections for this group. Publishing a View makes it available to all current group members.", }),
description: "i18n:govoplan-views.group_description",
canWrite: canWrite:
tenantManager || tenantManager ||
hasScope(auth, "views:group_definition:write") hasScope(auth, "views:group_definition:write")
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,63 @@
import type { DocumentationHelpReference } from "@govoplan/core-webui";
export const VIEWS_DOCUMENTATION = {
topicId: "views.interface-projections",
documentationType: "admin"
} satisfies DocumentationHelpReference;
export const VIEWS_FIELD_DOCUMENTATION = {
topicId: "views.reference.fields-and-consequences",
documentationType: "admin"
} satisfies DocumentationHelpReference;
export const VIEWS_INTERFACE_I18N = {
loadingText: "i18n:govoplan-views.loading",
busyText: "i18n:govoplan-views.busy",
definitionWriteText: "i18n:govoplan-views.definition_write_required",
assignmentWriteText: "i18n:govoplan-views.assignment_write_required",
missingCatalogueText: "i18n:govoplan-views.navigation_and_route_surfaces_required",
noChangesText: "i18n:govoplan-views.no_pending_changes",
nameRequiredText: "i18n:govoplan-views.name_required",
inheritedText: "i18n:govoplan-views.inherited_definition_read_only",
archivedText: "i18n:govoplan-views.archived_definition_read_only",
publishedDefinitionText: "i18n:govoplan-views.published_definition_required",
inheritedAssignmentText: "i18n:govoplan-views.inherited_assignment_read_only",
targetRequiredText: "i18n:govoplan-views.assignment_target_required",
lockoutText: "i18n:govoplan-views.required_view_must_keep_escape_surfaces",
readOnlySummaryText: "i18n:govoplan-views.views_administration_is_read_only",
requiredActionText: "i18n:govoplan-views.required_action",
actorText: "i18n:govoplan-views.responsible_actor",
destinationText: "i18n:govoplan-views.destination",
administratorText: "i18n:govoplan-views.view_manager_or_scope_owner",
permissionGuidanceText: "i18n:govoplan-views.request_view_management_permission",
administrationTargetText: "i18n:govoplan-views.views_administration",
directorySummaryText: "i18n:govoplan-views.assignment_directory_unavailable",
directoryGuidanceText: "i18n:govoplan-views.enable_access_or_choose_a_scope_without_a_directory_target",
moduleTargetText: "i18n:govoplan-views.module_administration"
} as const;
export function definitionDisabledReason({
loading = false,
busy,
permitted,
editable = true,
archived = false,
changed = true,
complete = true
}: {
loading?: boolean;
busy: boolean;
permitted: boolean;
editable?: boolean;
archived?: boolean;
changed?: boolean;
complete?: boolean;
}): string | undefined {
if (loading) return VIEWS_INTERFACE_I18N.loadingText;
if (busy) return VIEWS_INTERFACE_I18N.busyText;
if (!permitted) return VIEWS_INTERFACE_I18N.definitionWriteText;
if (!editable) return archived ? VIEWS_INTERFACE_I18N.archivedText : VIEWS_INTERFACE_I18N.inheritedText;
if (!complete) return VIEWS_INTERFACE_I18N.nameRequiredText;
if (!changed) return VIEWS_INTERFACE_I18N.noChangesText;
return undefined;
}
+264
View File
@@ -0,0 +1,264 @@
import type { PlatformTranslations } from "@govoplan/core-webui";
export const generatedTranslations: PlatformTranslations = {
en: {
"i18n:govoplan-views.views": "Views",
"i18n:govoplan-views.view_selector": "View selector",
"i18n:govoplan-views.system_views_administration": "System Views administration",
"i18n:govoplan-views.tenant_views_administration": "Tenant Views administration",
"i18n:govoplan-views.personal_and_group_views": "Personal and group Views",
"i18n:govoplan-views.view_selection_failed": "View selection failed.",
"i18n:govoplan-views.required_by_administrator": "This View is required by an administrator.",
"i18n:govoplan-views.selector_help": "Choose which parts of GovOPlaN are shown.",
"i18n:govoplan-views.current_view": "Current View",
"i18n:govoplan-views.full_interface": "Full interface",
"i18n:govoplan-views.no_personal_access": "You do not have access to personal or group View definitions.",
"i18n:govoplan-views.view_owner": "View owner",
"i18n:govoplan-views.my_views": "My Views",
"i18n:govoplan-views.personal_description": "Design personal interface projections. Publishing a View makes it available to this account.",
"i18n:govoplan-views.group_value": "Group: {value0}",
"i18n:govoplan-views.group_description": "Design reusable interface projections for this group. Publishing a View makes it available to all current group members.",
"i18n:govoplan-views.system_views": "System Views",
"i18n:govoplan-views.tenant_views": "Tenant Views",
"i18n:govoplan-views.group_views": "Group Views",
"i18n:govoplan-views.system_description": "Publish reusable interface projections and assign instance-wide defaults or requirements.",
"i18n:govoplan-views.tenant_description": "Tailor the visible interface for this tenant, its groups, and individual users.",
"i18n:govoplan-views.group_scope_description": "Design reusable interface projections for members of this group.",
"i18n:govoplan-views.user_scope_description": "Design personal interface projections that remain available to this account.",
"i18n:govoplan-views.loading": "View information is loading.",
"i18n:govoplan-views.busy": "A View operation is in progress.",
"i18n:govoplan-views.definition_write_required": "View-definition write permission is required.",
"i18n:govoplan-views.assignment_write_required": "View-assignment write permission is required.",
"i18n:govoplan-views.navigation_and_route_surfaces_required": "The installed surface catalogue must contain navigation and route surfaces.",
"i18n:govoplan-views.no_pending_changes": "Make a change before saving.",
"i18n:govoplan-views.name_required": "Enter a View name before continuing.",
"i18n:govoplan-views.inherited_definition_read_only": "Inherited View definitions are read-only in this scope.",
"i18n:govoplan-views.archived_definition_read_only": "Archived View definitions are read-only.",
"i18n:govoplan-views.published_definition_required": "Publish at least one View before creating an assignment.",
"i18n:govoplan-views.inherited_assignment_read_only": "This assignment is inherited from a higher scope and must be changed there.",
"i18n:govoplan-views.assignment_target_required": "Select an assignment target.",
"i18n:govoplan-views.assignment_target_immutable": "The target cannot be changed after this assignment is created.",
"i18n:govoplan-views.assignment_view_immutable": "The View cannot be changed after this assignment is created.",
"i18n:govoplan-views.required_view_must_keep_escape_surfaces": "A required View must retain the selector and administration escape surfaces.",
"i18n:govoplan-views.views_administration_is_read_only": "Views administration is read-only.",
"i18n:govoplan-views.required_action": "Required action",
"i18n:govoplan-views.responsible_actor": "Responsible actor",
"i18n:govoplan-views.destination": "Destination",
"i18n:govoplan-views.view_manager_or_scope_owner": "A View manager or owner of this scope",
"i18n:govoplan-views.request_view_management_permission": "Request the matching View-definition or assignment permission.",
"i18n:govoplan-views.views_administration": "Administration or Settings > Views",
"i18n:govoplan-views.assignment_directory_unavailable": "The assignment target directory is unavailable.",
"i18n:govoplan-views.enable_access_or_choose_a_scope_without_a_directory_target": "Enable the Access directory or choose a system or tenant target.",
"i18n:govoplan-views.module_administration": "Administration > Modules",
"i18n:govoplan-views.reload_views": "Reload Views",
"i18n:govoplan-views.new_view": "New View",
"i18n:govoplan-views.definitions": "Definitions",
"i18n:govoplan-views.view_definitions": "View definitions",
"i18n:govoplan-views.revision_value": "revision {value0}",
"i18n:govoplan-views.no_definitions": "No Views have been defined in this scope.",
"i18n:govoplan-views.inherited": "Inherited",
"i18n:govoplan-views.draft_revision_value": "Draft revision {value0}",
"i18n:govoplan-views.published_revision_value": "published revision {value0}",
"i18n:govoplan-views.not_published": "not published",
"i18n:govoplan-views.discard": "Discard",
"i18n:govoplan-views.save_revision": "Save revision",
"i18n:govoplan-views.publish": "Publish",
"i18n:govoplan-views.archive_view": "Archive View",
"i18n:govoplan-views.name": "Name",
"i18n:govoplan-views.description": "Description",
"i18n:govoplan-views.visible_surfaces": "Visible surfaces",
"i18n:govoplan-views.surface_security_help": "Permission checks remain active even when a surface is visible. Parent surfaces are retained automatically.",
"i18n:govoplan-views.selected_count": "{value0} selected",
"i18n:govoplan-views.stale_surfaces_value": "This revision references retired or currently unavailable surfaces: {value0}",
"i18n:govoplan-views.remove_stale_references": "Remove stale references",
"i18n:govoplan-views.no_view_selected": "No View selected",
"i18n:govoplan-views.create_projection_help": "Create a View to define a focused interface projection.",
"i18n:govoplan-views.create_view": "Create View",
"i18n:govoplan-views.cancel": "Cancel",
"i18n:govoplan-views.create": "Create",
"i18n:govoplan-views.first_draft_help": "The first draft starts with all current surfaces visible. Refine the selection before publishing it.",
"i18n:govoplan-views.archive_value_confirm": "Archive {value0}? Existing optional assignments will be deactivated. Required assignments must be removed first.",
"i18n:govoplan-views.archive": "Archive",
"i18n:govoplan-views.remove_assignment": "Remove View assignment",
"i18n:govoplan-views.remove_assignment_confirm": "The target will no longer inherit this available, default, or required View from this assignment.",
"i18n:govoplan-views.remove": "Remove",
"i18n:govoplan-views.draft_saved": "View draft saved.",
"i18n:govoplan-views.revision_published_value": "Revision {value0} published.",
"i18n:govoplan-views.draft_created": "View draft created with all currently available surfaces.",
"i18n:govoplan-views.view_archived": "View archived.",
"i18n:govoplan-views.assignment_created": "View assignment created.",
"i18n:govoplan-views.assignment_updated": "View assignment updated.",
"i18n:govoplan-views.assignment_removed": "View assignment removed.",
"i18n:govoplan-views.assignments": "Assignments",
"i18n:govoplan-views.assignment_precedence_help": "User and group assignments take precedence over tenant and system assignments. Required assignments cannot be left.",
"i18n:govoplan-views.add_assignment": "Add assignment",
"i18n:govoplan-views.unavailable_scope_value": "Unavailable {value0}",
"i18n:govoplan-views.priority_value": "Priority {value0}",
"i18n:govoplan-views.pinned_revision": "Pinned revision",
"i18n:govoplan-views.tracks_published": "Tracks published",
"i18n:govoplan-views.assignment_active": "Assignment active",
"i18n:govoplan-views.edit_assignment": "Edit assignment",
"i18n:govoplan-views.no_assignments": "No View assignments exist in this scope.",
"i18n:govoplan-views.add_view_assignment": "Add View assignment",
"i18n:govoplan-views.edit_view_assignment": "Edit View assignment",
"i18n:govoplan-views.save": "Save",
"i18n:govoplan-views.target_level": "Target level",
"i18n:govoplan-views.system": "System",
"i18n:govoplan-views.tenant": "Tenant",
"i18n:govoplan-views.group": "Group",
"i18n:govoplan-views.user": "User",
"i18n:govoplan-views.target": "Target",
"i18n:govoplan-views.select_scope_value": "Select {value0}",
"i18n:govoplan-views.select_a_scope_value": "Select a {value0}",
"i18n:govoplan-views.search_scope_value": "Search {value0}s",
"i18n:govoplan-views.no_matching_scope_value": "No matching {value0}s.",
"i18n:govoplan-views.access_directory_unavailable": "The Access directory is not available.",
"i18n:govoplan-views.current_system": "Current system",
"i18n:govoplan-views.current_tenant": "Current tenant",
"i18n:govoplan-views.view": "View",
"i18n:govoplan-views.mode": "Mode",
"i18n:govoplan-views.available": "Available",
"i18n:govoplan-views.default": "Default",
"i18n:govoplan-views.required": "Required",
"i18n:govoplan-views.priority": "Priority",
"i18n:govoplan-views.pin_revision": "Pin current published revision",
"i18n:govoplan-views.pin_revision_help": "Pinned assignments keep the selected revision. Unpinned assignments follow future published revisions.",
"i18n:govoplan-views.required_missing_value": "This revision cannot be required because it hides: {value0}.",
"i18n:govoplan-views.required_warning": "Required Views cannot be left by affected users. The selector and administration escape surfaces remain available.",
"i18n:govoplan-views.filter_surfaces": "Filter modules and surfaces",
"i18n:govoplan-views.filter_view_surfaces": "Filter View surfaces",
"i18n:govoplan-views.surface_count": "{value0}/{value1} surfaces",
"i18n:govoplan-views.surface_detail": "{value0} · {value1}",
"i18n:govoplan-views.no_matching_surfaces": "No matching surfaces."
},
de: {
"i18n:govoplan-views.views": "Ansichten",
"i18n:govoplan-views.view_selector": "Ansichtsauswahl",
"i18n:govoplan-views.system_views_administration": "Systemweite Ansichten verwalten",
"i18n:govoplan-views.tenant_views_administration": "Mandantenansichten verwalten",
"i18n:govoplan-views.personal_and_group_views": "Persönliche und Gruppenansichten",
"i18n:govoplan-views.view_selection_failed": "Die Ansicht konnte nicht ausgewählt werden.",
"i18n:govoplan-views.required_by_administrator": "Diese Ansicht wurde administrativ vorgeschrieben.",
"i18n:govoplan-views.selector_help": "Wählen Sie aus, welche Teile von GovOPlaN angezeigt werden.",
"i18n:govoplan-views.current_view": "Aktuelle Ansicht",
"i18n:govoplan-views.full_interface": "Vollständige Oberfläche",
"i18n:govoplan-views.no_personal_access": "Sie haben keinen Zugriff auf persönliche oder gruppeneigene Ansichtsdefinitionen.",
"i18n:govoplan-views.view_owner": "Eigentümerschaft der Ansicht",
"i18n:govoplan-views.my_views": "Meine Ansichten",
"i18n:govoplan-views.personal_description": "Entwerfen Sie persönliche Oberflächenansichten. Durch Veröffentlichung wird die Ansicht für dieses Konto verfügbar.",
"i18n:govoplan-views.group_value": "Gruppe: {value0}",
"i18n:govoplan-views.group_description": "Entwerfen Sie wiederverwendbare Oberflächenansichten für diese Gruppe. Durch Veröffentlichung wird die Ansicht für alle aktuellen Gruppenmitglieder verfügbar.",
"i18n:govoplan-views.system_views": "Systemansichten",
"i18n:govoplan-views.tenant_views": "Mandantenansichten",
"i18n:govoplan-views.group_views": "Gruppenansichten",
"i18n:govoplan-views.system_description": "Veröffentlichen Sie wiederverwendbare Oberflächenansichten und weisen Sie systemweite Vorgaben zu.",
"i18n:govoplan-views.tenant_description": "Passen Sie die sichtbare Oberfläche für diesen Mandanten, seine Gruppen und einzelne Benutzer an.",
"i18n:govoplan-views.group_scope_description": "Entwerfen Sie wiederverwendbare Oberflächenansichten für Mitglieder dieser Gruppe.",
"i18n:govoplan-views.user_scope_description": "Entwerfen Sie persönliche Oberflächenansichten für dieses Konto.",
"i18n:govoplan-views.loading": "Ansichtsinformationen werden geladen.",
"i18n:govoplan-views.busy": "Eine Ansichtsoperation wird gerade ausgeführt.",
"i18n:govoplan-views.definition_write_required": "Die Schreibberechtigung für Ansichtsdefinitionen ist erforderlich.",
"i18n:govoplan-views.assignment_write_required": "Die Schreibberechtigung für Ansichtszuweisungen ist erforderlich.",
"i18n:govoplan-views.navigation_and_route_surfaces_required": "Der installierte Oberflächenkatalog muss Navigations- und Routeneinträge enthalten.",
"i18n:govoplan-views.no_pending_changes": "Nehmen Sie vor dem Speichern eine Änderung vor.",
"i18n:govoplan-views.name_required": "Geben Sie einen Ansichtsnamen ein.",
"i18n:govoplan-views.inherited_definition_read_only": "Geerbte Ansichtsdefinitionen sind in diesem Bereich schreibgeschützt.",
"i18n:govoplan-views.archived_definition_read_only": "Archivierte Ansichtsdefinitionen sind schreibgeschützt.",
"i18n:govoplan-views.published_definition_required": "Veröffentlichen Sie mindestens eine Ansicht, bevor Sie eine Zuweisung erstellen.",
"i18n:govoplan-views.inherited_assignment_read_only": "Diese Zuweisung wurde aus einem höheren Bereich geerbt und muss dort geändert werden.",
"i18n:govoplan-views.assignment_target_required": "Wählen Sie ein Zuweisungsziel aus.",
"i18n:govoplan-views.assignment_target_immutable": "Das Ziel kann nach dem Erstellen dieser Zuweisung nicht geändert werden.",
"i18n:govoplan-views.assignment_view_immutable": "Die Ansicht kann nach dem Erstellen dieser Zuweisung nicht geändert werden.",
"i18n:govoplan-views.required_view_must_keep_escape_surfaces": "Eine vorgeschriebene Ansicht muss Auswahl und administrative Rückkehrmöglichkeiten enthalten.",
"i18n:govoplan-views.views_administration_is_read_only": "Die Ansichtsverwaltung ist schreibgeschützt.",
"i18n:govoplan-views.required_action": "Erforderliche Aktion",
"i18n:govoplan-views.responsible_actor": "Zuständige Stelle",
"i18n:govoplan-views.destination": "Ziel",
"i18n:govoplan-views.view_manager_or_scope_owner": "Eine Ansichtsverwaltung oder Eigentümerschaft dieses Bereichs",
"i18n:govoplan-views.request_view_management_permission": "Fordern Sie die passende Berechtigung für Ansichtsdefinitionen oder -zuweisungen an.",
"i18n:govoplan-views.views_administration": "Administration oder Einstellungen > Ansichten",
"i18n:govoplan-views.assignment_directory_unavailable": "Das Verzeichnis der Zuweisungsziele ist nicht verfügbar.",
"i18n:govoplan-views.enable_access_or_choose_a_scope_without_a_directory_target": "Aktivieren Sie Access oder wählen Sie ein System- oder Mandantenziel.",
"i18n:govoplan-views.module_administration": "Administration > Module",
"i18n:govoplan-views.reload_views": "Ansichten neu laden",
"i18n:govoplan-views.new_view": "Neue Ansicht",
"i18n:govoplan-views.definitions": "Definitionen",
"i18n:govoplan-views.view_definitions": "Ansichtsdefinitionen",
"i18n:govoplan-views.revision_value": "Revision {value0}",
"i18n:govoplan-views.no_definitions": "In diesem Bereich wurden keine Ansichten definiert.",
"i18n:govoplan-views.inherited": "Geerbt",
"i18n:govoplan-views.draft_revision_value": "Entwurfsrevision {value0}",
"i18n:govoplan-views.published_revision_value": "veröffentlichte Revision {value0}",
"i18n:govoplan-views.not_published": "nicht veröffentlicht",
"i18n:govoplan-views.discard": "Verwerfen",
"i18n:govoplan-views.save_revision": "Revision speichern",
"i18n:govoplan-views.publish": "Veröffentlichen",
"i18n:govoplan-views.archive_view": "Ansicht archivieren",
"i18n:govoplan-views.name": "Name",
"i18n:govoplan-views.description": "Beschreibung",
"i18n:govoplan-views.visible_surfaces": "Sichtbare Oberflächen",
"i18n:govoplan-views.surface_security_help": "Berechtigungsprüfungen bleiben auch bei sichtbaren Oberflächen aktiv. Übergeordnete Oberflächen werden automatisch beibehalten.",
"i18n:govoplan-views.selected_count": "{value0} ausgewählt",
"i18n:govoplan-views.stale_surfaces_value": "Diese Revision verweist auf entfernte oder derzeit nicht verfügbare Oberflächen: {value0}",
"i18n:govoplan-views.remove_stale_references": "Veraltete Verweise entfernen",
"i18n:govoplan-views.no_view_selected": "Keine Ansicht ausgewählt",
"i18n:govoplan-views.create_projection_help": "Erstellen Sie eine Ansicht, um eine fokussierte Oberfläche zu definieren.",
"i18n:govoplan-views.create_view": "Ansicht erstellen",
"i18n:govoplan-views.cancel": "Abbrechen",
"i18n:govoplan-views.create": "Erstellen",
"i18n:govoplan-views.first_draft_help": "Der erste Entwurf zeigt alle aktuellen Oberflächen. Grenzen Sie die Auswahl vor der Veröffentlichung ein.",
"i18n:govoplan-views.archive_value_confirm": "{value0} archivieren? Bestehende optionale Zuweisungen werden deaktiviert. Vorgeschriebene Zuweisungen müssen zuerst entfernt werden.",
"i18n:govoplan-views.archive": "Archivieren",
"i18n:govoplan-views.remove_assignment": "Ansichtszuweisung entfernen",
"i18n:govoplan-views.remove_assignment_confirm": "Das Ziel erbt diese verfügbare, standardmäßige oder vorgeschriebene Ansicht anschließend nicht mehr aus dieser Zuweisung.",
"i18n:govoplan-views.remove": "Entfernen",
"i18n:govoplan-views.draft_saved": "Ansichtsentwurf gespeichert.",
"i18n:govoplan-views.revision_published_value": "Revision {value0} veröffentlicht.",
"i18n:govoplan-views.draft_created": "Ansichtsentwurf mit allen derzeit verfügbaren Oberflächen erstellt.",
"i18n:govoplan-views.view_archived": "Ansicht archiviert.",
"i18n:govoplan-views.assignment_created": "Ansichtszuweisung erstellt.",
"i18n:govoplan-views.assignment_updated": "Ansichtszuweisung aktualisiert.",
"i18n:govoplan-views.assignment_removed": "Ansichtszuweisung entfernt.",
"i18n:govoplan-views.assignments": "Zuweisungen",
"i18n:govoplan-views.assignment_precedence_help": "Benutzer- und Gruppenzuweisungen haben Vorrang vor Mandanten- und Systemzuweisungen. Vorgeschriebene Zuweisungen können nicht verlassen werden.",
"i18n:govoplan-views.add_assignment": "Zuweisung hinzufügen",
"i18n:govoplan-views.unavailable_scope_value": "{value0} nicht verfügbar",
"i18n:govoplan-views.priority_value": "Priorität {value0}",
"i18n:govoplan-views.pinned_revision": "Revision angeheftet",
"i18n:govoplan-views.tracks_published": "Folgt der Veröffentlichung",
"i18n:govoplan-views.assignment_active": "Zuweisung aktiv",
"i18n:govoplan-views.edit_assignment": "Zuweisung bearbeiten",
"i18n:govoplan-views.no_assignments": "In diesem Bereich bestehen keine Ansichtszuweisungen.",
"i18n:govoplan-views.add_view_assignment": "Ansichtszuweisung hinzufügen",
"i18n:govoplan-views.edit_view_assignment": "Ansichtszuweisung bearbeiten",
"i18n:govoplan-views.save": "Speichern",
"i18n:govoplan-views.target_level": "Zielebene",
"i18n:govoplan-views.system": "System",
"i18n:govoplan-views.tenant": "Mandant",
"i18n:govoplan-views.group": "Gruppe",
"i18n:govoplan-views.user": "Benutzer",
"i18n:govoplan-views.target": "Ziel",
"i18n:govoplan-views.select_scope_value": "{value0} auswählen",
"i18n:govoplan-views.select_a_scope_value": "{value0} auswählen",
"i18n:govoplan-views.search_scope_value": "{value0} suchen",
"i18n:govoplan-views.no_matching_scope_value": "Keine passenden Einträge für {value0}.",
"i18n:govoplan-views.access_directory_unavailable": "Das Access-Verzeichnis ist nicht verfügbar.",
"i18n:govoplan-views.current_system": "Aktuelles System",
"i18n:govoplan-views.current_tenant": "Aktueller Mandant",
"i18n:govoplan-views.view": "Ansicht",
"i18n:govoplan-views.mode": "Modus",
"i18n:govoplan-views.available": "Verfügbar",
"i18n:govoplan-views.default": "Standard",
"i18n:govoplan-views.required": "Vorgeschrieben",
"i18n:govoplan-views.priority": "Priorität",
"i18n:govoplan-views.pin_revision": "Aktuelle veröffentlichte Revision anheften",
"i18n:govoplan-views.pin_revision_help": "Angeheftete Zuweisungen behalten die ausgewählte Revision. Nicht angeheftete Zuweisungen folgen späteren Veröffentlichungen.",
"i18n:govoplan-views.required_missing_value": "Diese Revision kann nicht vorgeschrieben werden, weil sie folgende Oberflächen ausblendet: {value0}.",
"i18n:govoplan-views.required_warning": "Vorgeschriebene Ansichten können von betroffenen Benutzern nicht verlassen werden. Auswahl und administrative Rückkehrmöglichkeiten bleiben verfügbar.",
"i18n:govoplan-views.filter_surfaces": "Module und Oberflächen filtern",
"i18n:govoplan-views.filter_view_surfaces": "Ansichtsoberflächen filtern",
"i18n:govoplan-views.surface_count": "{value0}/{value1} Oberflächen",
"i18n:govoplan-views.surface_detail": "{value0} · {value1}",
"i18n:govoplan-views.no_matching_surfaces": "Keine passenden Oberflächen."
}
};
+13 -8
View File
@@ -12,6 +12,7 @@ import {
resolveWorkflowView, resolveWorkflowView,
selectEffectiveView selectEffectiveView
} from "./api/views"; } from "./api/views";
import { generatedTranslations } from "./i18n/generatedTranslations";
import "./styles/views.css"; import "./styles/views.css";
@@ -28,7 +29,7 @@ const viewsAdminSections: AdminSectionsUiCapability = {
id: "system-views", id: "system-views",
moduleId: "views", moduleId: "views",
kind: "settings", kind: "settings",
label: "Views", label: "i18n:govoplan-views.views",
group: "SYSTEM", group: "SYSTEM",
order: 15, order: 15,
surfaceId: "views.admin.system", surfaceId: "views.admin.system",
@@ -49,7 +50,7 @@ const viewsAdminSections: AdminSectionsUiCapability = {
id: "tenant-views", id: "tenant-views",
moduleId: "views", moduleId: "views",
kind: "settings", kind: "settings",
label: "Views", label: "i18n:govoplan-views.views",
group: "TENANT", group: "TENANT",
order: 15, order: 15,
surfaceId: "views.admin.tenant", surfaceId: "views.admin.tenant",
@@ -81,7 +82,7 @@ const viewsSettingsSections: SettingsSectionsUiCapability = {
sections: [ sections: [
{ {
id: "views", id: "views",
label: "Views", label: "i18n:govoplan-views.views",
group: "ui", group: "ui",
order: 35, order: 35,
surfaceId: "views.settings.personal", surfaceId: "views.settings.personal",
@@ -101,15 +102,19 @@ const viewsSettingsSections: SettingsSectionsUiCapability = {
export const viewsModule: PlatformWebModule = { export const viewsModule: PlatformWebModule = {
id: "views", id: "views",
label: "Views", label: "i18n:govoplan-views.views",
version: "0.1.0", version: "0.1.0",
optionalDependencies: ["access", "admin", "policy", "workflow"], optionalDependencies: ["access", "admin", "policy", "workflow"],
translations: {
en: generatedTranslations.en,
de: generatedTranslations.de
},
viewSurfaces: [ viewSurfaces: [
{ {
id: "views.selector", id: "views.selector",
moduleId: "views", moduleId: "views",
kind: "selector", kind: "selector",
label: "View selector", label: "i18n:govoplan-views.view_selector",
order: 1, order: 1,
required: true required: true
}, },
@@ -117,21 +122,21 @@ export const viewsModule: PlatformWebModule = {
id: "views.admin.system", id: "views.admin.system",
moduleId: "views", moduleId: "views",
kind: "section", kind: "section",
label: "System Views administration", label: "i18n:govoplan-views.system_views_administration",
order: 20 order: 20
}, },
{ {
id: "views.admin.tenant", id: "views.admin.tenant",
moduleId: "views", moduleId: "views",
kind: "section", kind: "section",
label: "Tenant Views administration", label: "i18n:govoplan-views.tenant_views_administration",
order: 30 order: 30
}, },
{ {
id: "views.settings.personal", id: "views.settings.personal",
moduleId: "views", moduleId: "views",
kind: "section", kind: "section",
label: "Personal and group Views", label: "i18n:govoplan-views.personal_and_group_views",
order: 40 order: 40
} }
], ],