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.
This commit is contained in:
2026-09-09 02:03:46 +02:00
parent 61625fb00f
commit ca99d0d584
7 changed files with 21 additions and 10 deletions
@@ -56,6 +56,9 @@ _TRANSLATIONS = {
"title": "Verwaltete Dateien und Ordner organisieren", "title": "Verwaltete Dateien und Ordner organisieren",
"summary": "Ordner anlegen und zugängliche Inhalte mit ausdrücklicher Konfliktbehandlung umbenennen, verschieben oder kopieren.", "summary": "Ordner anlegen und zugängliche Inhalte mit ausdrücklicher Konfliktbehandlung umbenennen, verschieben oder kopieren.",
"body": ( "body": (
"Dokumentationsbücher stehen neben den Überschriften für Dateien, Verbindungen, Richtlinien "
"oder Integrität und neben dem Titel des Dialogs zum Entpacken verwalteter Archive. "
"Feldhilfe bleibt bei der Feldbezeichnung. "
"Die Organisation bleibt in gesteuerten persönlichen oder Gruppenbereichen. Verschieben erhält die Asset-Identität; Kopieren erzeugt neue Assets und Versionen, die unveränderliche Blob-Bytes wiederverwenden. Jeder Zielkonflikt muss ausdrücklich abgelehnt, durch Umbenennen gelöst, überschrieben oder übersprungen werden. " "Die Organisation bleibt in gesteuerten persönlichen oder Gruppenbereichen. Verschieben erhält die Asset-Identität; Kopieren erzeugt neue Assets und Versionen, die unveränderliche Blob-Bytes wiederverwenden. Jeder Zielkonflikt muss ausdrücklich abgelehnt, durch Umbenennen gelöst, überschrieben oder übersprungen werden. "
"Ordner erstellen und Hochladen bleiben neben Neu laden im Kopf des Arbeitsbereichs. Nach der Auswahl von Dateien oder Ordnern öffnet Auswahl verwalten die Aktionen Verschieben, Kopieren, Umbenennen, Freigaben und Zugriffserklärungen. " "Ordner erstellen und Hochladen bleiben neben Neu laden im Kopf des Arbeitsbereichs. Nach der Auswahl von Dateien oder Ordnern öffnet Auswahl verwalten die Aktionen Verschieben, Kopieren, Umbenennen, Freigaben und Zugriffserklärungen. "
"Löschen ist von der Organisation getrennt und benötigt weiterhin die vorhandene Bestätigung. Herunterladen und Archiv entpacken bleiben für passende Auswahlen direkt erreichbar. " "Löschen ist von der Organisation getrennt und benötigt weiterhin die vorhandene Bestätigung. Herunterladen und Archiv entpacken bleiben für passende Auswahlen direkt erreichbar. "
+7
View File
@@ -1058,6 +1058,8 @@ manifest = ModuleManifest(
title="Organize managed files and folders", title="Organize managed files and folders",
summary="Create folders and rename, move, or copy accessible managed content with explicit conflict handling.", summary="Create folders and rename, move, or copy accessible managed content with explicit conflict handling.",
body=( body=(
"Documentation books sit beside the Files, connection, policy, or integrity heading and "
"beside the managed-archive unpack dialog title. Field help stays with its label. "
"Organization stays inside governed personal or group spaces. Moves preserve the asset identity, while copies create new assets and versions that reuse immutable blob bytes. " "Organization stays inside governed personal or group spaces. Moves preserve the asset identity, while copies create new assets and versions that reuse immutable blob bytes. "
"Every target conflict must be rejected, renamed, overwritten, or skipped explicitly. " "Every target conflict must be rejected, renamed, overwritten, or skipped explicitly. "
"Create folder and Upload remain beside Reload in the workspace header. Select files or folders and open Manage selection for Move, Copy, Rename, sharing, or access explanations. " "Create folder and Upload remain beside Reload in the workspace header. Select files or folders and open Manage selection for Move, Copy, Rename, sharing, or access explanations. "
@@ -1458,6 +1460,8 @@ manifest = ModuleManifest(
title="Govern file connections, folder sync, and credential deletion", title="Govern file connections, folder sync, and credential deletion",
summary="Keep endpoints, credentials, inherited policy, and bounded manual synchronization separate, with reviewable outcomes and provenance.", summary="Keep endpoints, credentials, inherited policy, and bounded manual synchronization separate, with reviewable outcomes and provenance.",
body=( body=(
"Documentation books sit beside the Files, connection, policy, or integrity heading and "
"beside the managed-archive unpack dialog title. Field help stays with its label. "
"System, tenant, and one user/group/campaign leaf form the effective policy chain: deny rules win and every configured allow rule must match. " "System, tenant, and one user/group/campaign leaf form the effective policy chain: deny rules win and every configured allow rule must match. "
"Responses redact secret values and deployment references. Deleting a database-managed credential or profile immediately scrubs Files-owned encrypted material and private metadata in the same transaction as a non-secret audit event; dependent profiles are disabled, while legacy non-owned references are only detached and audited. " "Responses redact secret values and deployment references. Deleting a database-managed credential or profile immediately scrubs Files-owned encrypted material and private metadata in the same transaction as a non-secret audit event; dependent profiles are disabled, while legacy non-owned references are only detached and audited. "
"Removing a connector space is a separate owner-authorized operation: it retires only the local virtual-space link and leaves provider content, imported managed files and shares, profiles, credentials, and remote references untouched. Intrinsic user and group managed spaces cannot be removed. " "Removing a connector space is a separate owner-authorized operation: it retires only the local virtual-space link and leaves provider content, imported managed files and shares, profiles, credentials, and remote references untouched. Intrinsic user and group managed spaces cannot be removed. "
@@ -1517,6 +1521,9 @@ manifest = ModuleManifest(
"title": "Dateiverbindungen, Ordnersynchronisierung und das Löschen von Zugangsdaten steuern", "title": "Dateiverbindungen, Ordnersynchronisierung und das Löschen von Zugangsdaten steuern",
"summary": "Endpunkte, Zugangsdaten, vererbte Richtlinien und begrenzte manuelle Synchronisierung getrennt und mit prüfbaren Ergebnissen sowie Herkunftsnachweisen verwalten.", "summary": "Endpunkte, Zugangsdaten, vererbte Richtlinien und begrenzte manuelle Synchronisierung getrennt und mit prüfbaren Ergebnissen sowie Herkunftsnachweisen verwalten.",
"body": ( "body": (
"Dokumentationsbücher stehen neben den Überschriften für Dateien, Verbindungen, Richtlinien "
"oder Integrität und neben dem Titel des Dialogs zum Entpacken verwalteter Archive. Feldhilfe "
"bleibt bei der Feldbezeichnung. "
"System, Mandant und genau eine Benutzer-, Gruppen- oder Kampagnenebene bilden die wirksame Richtlinienkette: Ablehnungsregeln haben Vorrang und jede konfigurierte Erlaubnisregel muss zutreffen. " "System, Mandant und genau eine Benutzer-, Gruppen- oder Kampagnenebene bilden die wirksame Richtlinienkette: Ablehnungsregeln haben Vorrang und jede konfigurierte Erlaubnisregel muss zutreffen. "
"Antworten blenden Geheimwerte und Bereitstellungsverweise aus. Beim Löschen datenbankverwalteter Zugangsdaten oder Profile entfernt Files eigenes verschlüsseltes Material und private Metadaten in derselben Transaktion wie das nicht geheime Audit-Ereignis. Abhängige Profile werden deaktiviert; ältere, nicht Files gehörende Verweise werden nur getrennt und auditiert. " "Antworten blenden Geheimwerte und Bereitstellungsverweise aus. Beim Löschen datenbankverwalteter Zugangsdaten oder Profile entfernt Files eigenes verschlüsseltes Material und private Metadaten in derselben Transaktion wie das nicht geheime Audit-Ereignis. Abhängige Profile werden deaktiviert; ältere, nicht Files gehörende Verweise werden nur getrennt und auditiert. "
"Das Entfernen eines Connector-Bereichs ist ein eigener, eigentümerberechtigter Vorgang: Nur die lokale Verknüpfung des virtuellen Bereichs wird außer Kraft gesetzt. Inhalte beim Anbieter, importierte verwaltete Dateien und Freigaben, Profile, Zugangsdaten und Remote-Verweise bleiben erhalten. Intrinsische Benutzer- und Gruppenbereiche können nicht entfernt werden. " "Das Entfernen eines Connector-Bereichs ist ein eigener, eigentümerberechtigter Vorgang: Nur die lokale Verknüpfung des virtuellen Bereichs wird außer Kraft gesetzt. Inhalte beim Anbieter, importierte verwaltete Dateien und Freigaben, Profile, Zugangsdaten und Remote-Verweise bleiben erhalten. Intrinsische Benutzer- und Gruppenbereiche können nicht entfernt werden. "
@@ -39,7 +39,7 @@ assert.match(filesPage, /disabledReason=\{uploadBlocker\}/);
assert.match(filesPage, /disabledReason=\{deleteBlocker\}/); assert.match(filesPage, /disabledReason=\{deleteBlocker\}/);
assert.match(filesPage, /<ConfirmDialog[\s\S]*tone="danger"/); assert.match(filesPage, /<ConfirmDialog[\s\S]*tone="danger"/);
assert.match(filesPage, /<WorkspaceFrame as="main" height="viewport" surface="plain"/); assert.match(filesPage, /<WorkspaceFrame as="main" height="viewport" surface="plain"/);
assert.match(filesPage, /const toolbar = <WorkspaceActionBar\s+scope="workspace"\s+variant="collection"\s+refreshable/); assert.match(filesPage, /const toolbar = <WorkspaceActionBar\s+title="i18n:govoplan-files\.files\.6ce6c512"\s+titleHelp=\{<DocumentationHelpLink reference=\{FILES_WORKFLOW_DOCUMENTATION\} \/>\}\s+scope="workspace"\s+variant="collection"\s+refreshable/);
assert.match(filesPage, /\{toolbar\}[\s\S]*className=\{`file-manager-shell/); assert.match(filesPage, /\{toolbar\}[\s\S]*className=\{`file-manager-shell/);
assert.match(filesPage, /const selectionToolbar = <WorkspaceActionBar\s+scope="detail-pane"/); assert.match(filesPage, /const selectionToolbar = <WorkspaceActionBar\s+scope="detail-pane"/);
assert.match(filesPage, /<Dialog\s+open=\{toolsPanel !== null\}/); assert.match(filesPage, /<Dialog\s+open=\{toolsPanel !== null\}/);
@@ -931,14 +931,14 @@ export default function FileConnectorSettingsPanel({
<> <>
<Card <Card
title={panelTitle} title={panelTitle}
titleHelp={<DocumentationHelpLink reference={CONNECTOR_DOCUMENTATION} />}
actions={ actions={
canWrite ? canWrite ?
<div className="button-row compact-actions"> <div className="button-row compact-actions">
<DocumentationHelpLink reference={CONNECTOR_DOCUMENTATION} />
<Button onClick={() => void loadProfiles()} disabled={loading || saving} disabledReason={loading ? "File connections are already loading." : saving ? "Wait for the current connector change to finish." : undefined}>{loading ? "i18n:govoplan-files.loading.b04ba49f" : "i18n:govoplan-files.reload.cce71553"}</Button> <Button onClick={() => void loadProfiles()} disabled={loading || saving} disabledReason={loading ? "File connections are already loading." : saving ? "Wait for the current connector change to finish." : undefined}>{loading ? "i18n:govoplan-files.loading.b04ba49f" : "i18n:govoplan-files.reload.cce71553"}</Button>
<Button variant="primary" onClick={startCreate} disabled={saving} disabledReason={saving ? "Wait for the current connector change to finish." : undefined}><Plus size={16} aria-hidden="true" /> i18n:govoplan-files.new_connection.ac979fe4</Button> <Button variant="primary" onClick={startCreate} disabled={saving} disabledReason={saving ? "Wait for the current connector change to finish." : undefined}><Plus size={16} aria-hidden="true" /> i18n:govoplan-files.new_connection.ac979fe4</Button>
</div> : </div> :
<DocumentationHelpLink reference={CONNECTOR_DOCUMENTATION} /> undefined
}> }>
<LoadingFrame loading={loading} label="i18n:govoplan-files.loading_file_connections.bd68f224"> <LoadingFrame loading={loading} label="i18n:govoplan-files.loading_file_connections.bd68f224">
@@ -954,14 +954,14 @@ export default function FileConnectorSettingsPanel({
<Card <Card
title={i18nMessage("i18n:govoplan-files.value_connector_policy.0bf7f53b", { value0: scopeLabel(scopeType) })} title={i18nMessage("i18n:govoplan-files.value_connector_policy.0bf7f53b", { value0: scopeLabel(scopeType) })}
titleHelp={<DocumentationHelpLink reference={CONNECTOR_DOCUMENTATION} />}
actions={canWrite ? actions={canWrite ?
<div className="button-row compact-actions"> <div className="button-row compact-actions">
<DocumentationHelpLink reference={CONNECTOR_DOCUMENTATION} />
<Button variant="primary" onClick={() => void savePolicyDraft()} disabled={saving || loading} disabledReason={saving ? "Wait for the current connector change to finish." : loading ? "Wait until the effective connector policy has loaded." : undefined}> <Button variant="primary" onClick={() => void savePolicyDraft()} disabled={saving || loading} disabledReason={saving ? "Wait for the current connector change to finish." : loading ? "Wait until the effective connector policy has loaded." : undefined}>
<ShieldCheck size={16} aria-hidden="true" /> {saving ? "i18n:govoplan-files.saving.ae7e8875" : "i18n:govoplan-files.save_policy.77d67ce3"} <ShieldCheck size={16} aria-hidden="true" /> {saving ? "i18n:govoplan-files.saving.ae7e8875" : "i18n:govoplan-files.save_policy.77d67ce3"}
</Button> </Button>
</div> : </div> :
<DocumentationHelpLink reference={CONNECTOR_DOCUMENTATION} />}> undefined}>
<LoadingFrame loading={loading} label="i18n:govoplan-files.loading_connector_policy.f12ab285"> <LoadingFrame loading={loading} label="i18n:govoplan-files.loading_connector_policy.f12ab285">
<> <>
@@ -384,6 +384,7 @@ export default function FileIntegrityPanel({ settings, canWrite }: Props) {
<> <>
<AdminPageLayout <AdminPageLayout
title="File integrity" title="File integrity"
titleHelp={<DocumentationHelpLink reference={DOCUMENTATION} label="Open Files integrity documentation" />}
description="Run bounded storage reconciliation and resolve quarantined or unreferenced objects without acting on stale operator state." description="Run bounded storage reconciliation and resolve quarantined or unreferenced objects without acting on stale operator state."
loading={loading} loading={loading}
error={error} error={error}
@@ -401,7 +402,6 @@ export default function FileIntegrityPanel({ settings, canWrite }: Props) {
<Button variant="primary" onClick={() => setCreateOpen(true)} disabled={!canWrite || busy}> <Button variant="primary" onClick={() => setCreateOpen(true)} disabled={!canWrite || busy}>
<Plus size={16} /> New scan <Plus size={16} /> New scan
</Button> </Button>
<DocumentationHelpLink reference={DOCUMENTATION} label="Open Files integrity documentation" />
</> </>
)} )}
> >
+3 -3
View File
@@ -2421,6 +2421,8 @@ export default function FilesPage({ settings, auth }: {settings: ApiSettings;aut
} }
const toolbar = <WorkspaceActionBar const toolbar = <WorkspaceActionBar
title="i18n:govoplan-files.files.6ce6c512"
titleHelp={<DocumentationHelpLink reference={FILES_WORKFLOW_DOCUMENTATION} />}
scope="workspace" scope="workspace"
variant="collection" variant="collection"
refreshable refreshable
@@ -2430,7 +2432,6 @@ export default function FilesPage({ settings, auth }: {settings: ApiSettings;aut
helpModuleId="files" helpModuleId="files"
reloadAction={{ onReload: () => void reloadCurrentView(), loading: reloadingView, state: viewReloadFailed ? "reload-failed" : "current", disabled: busy || connectorSpaceLoading, disabledReason: workingBlocker || (connectorSpaceLoading ? "This connector space is already refreshing." : undefined) }} reloadAction={{ onReload: () => void reloadCurrentView(), loading: reloadingView, state: viewReloadFailed ? "reload-failed" : "current", disabled: busy || connectorSpaceLoading, disabledReason: workingBlocker || (connectorSpaceLoading ? "This connector space is already refreshing." : undefined) }}
contextActions={<Button onClick={() => setToolsPanel("connections")} disabled={busy} disabledReason={workingBlocker}><Settings2 size={16} aria-hidden="true" /> i18n:govoplan-files.tools.connections</Button>} contextActions={<Button onClick={() => setToolsPanel("connections")} disabled={busy} disabledReason={workingBlocker}><Settings2 size={16} aria-hidden="true" /> i18n:govoplan-files.tools.connections</Button>}
helpAction={<DocumentationHelpLink reference={FILES_WORKFLOW_DOCUMENTATION} />}
createAction={<> createAction={<>
<Button onClick={() => openDialog("create-folder", toolbarTarget())} disabled={Boolean(organizeBlocker)} disabledReason={organizeBlocker}><Plus size={16} aria-hidden="true" /> i18n:govoplan-files.create_folder.97bafaba</Button> <Button onClick={() => openDialog("create-folder", toolbarTarget())} disabled={Boolean(organizeBlocker)} disabledReason={organizeBlocker}><Plus size={16} aria-hidden="true" /> i18n:govoplan-files.create_folder.97bafaba</Button>
<Button variant="primary" onClick={() => openDialog("upload", toolbarTarget())} disabled={Boolean(uploadBlocker)} disabledReason={uploadBlocker}><UploadCloud size={16} aria-hidden="true" /> i18n:govoplan-files.upload.8bdf057f</Button> <Button variant="primary" onClick={() => openDialog("upload", toolbarTarget())} disabled={Boolean(uploadBlocker)} disabledReason={uploadBlocker}><UploadCloud size={16} aria-hidden="true" /> i18n:govoplan-files.upload.8bdf057f</Button>
@@ -2928,12 +2929,11 @@ export default function FilesPage({ settings, auth }: {settings: ApiSettings;aut
} }
{dialog === "upload" && {dialog === "upload" &&
<FileDialog title={managedArchiveFile ? "i18n:govoplan-files.managed_archive.unpack" : i18nMessage("i18n:govoplan-files.upload_to_value_value.b83a34b4", { value0: activeDialogSpace?.label || "i18n:govoplan-files.files.6ce6c512", value1: activeDialogTarget?.folderPath || "i18n:govoplan-files.root.e96857c5" })} busy={busy || uploadActive} onClose={() => { if (!busy) closeDialog(); }}> <FileDialog title={managedArchiveFile ? "i18n:govoplan-files.managed_archive.unpack" : i18nMessage("i18n:govoplan-files.upload_to_value_value.b83a34b4", { value0: activeDialogSpace?.label || "i18n:govoplan-files.files.6ce6c512", value1: activeDialogTarget?.folderPath || "i18n:govoplan-files.root.e96857c5" })} titleHelp={managedArchiveFile ? <DocumentationHelpLink reference={{ topicId: "files.workflow.unpack-managed-archive", documentationType: "user" }} /> : undefined} busy={busy || uploadActive} onClose={() => { if (!busy) closeDialog(); }}>
<LoadingFrame loading={uploadActive} label={operationBusyLabel} indicator="none" progress={operationProgressValue} progressLabel={operationProgressLabel}> <LoadingFrame loading={uploadActive} label={operationBusyLabel} indicator="none" progress={operationProgressValue} progressLabel={operationProgressLabel}>
<div inert={uploadActive}> <div inert={uploadActive}>
{managedArchiveFile && <p className="form-help">{i18nMessage("i18n:govoplan-files.managed_archive.source", { value0: managedArchiveFile.filename })}</p>} {managedArchiveFile && <p className="form-help">{i18nMessage("i18n:govoplan-files.managed_archive.source", { value0: managedArchiveFile.filename })}</p>}
{managedArchiveFile && <p className="form-help">i18n:govoplan-files.managed_archive.protection</p>} {managedArchiveFile && <p className="form-help">i18n:govoplan-files.managed_archive.protection</p>}
{managedArchiveFile && <DocumentationHelpLink reference={{ topicId: "files.workflow.unpack-managed-archive", documentationType: "user" }} />}
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>} {error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}
{!archivePreview && {!archivePreview &&
<> <>
@@ -195,11 +195,12 @@ export function RenamePreviewList({
} }
export function FileDialog({ title, onClose, children, busy = false }: {title: string;onClose: () => void;children: ReactNode;busy?: boolean;}) { export function FileDialog({ title, titleHelp, onClose, children, busy = false }: {title: string;titleHelp?: ReactNode;onClose: () => void;children: ReactNode;busy?: boolean;}) {
return ( return (
<Dialog <Dialog
open open
title={title} title={title}
titleHelp={titleHelp}
onClose={onClose} onClose={onClose}
closeDisabled={busy} closeDisabled={busy}
closeOnBackdrop={!busy} closeOnBackdrop={!busy}