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:
@@ -1075,7 +1075,9 @@ manifest = ModuleManifest(
|
||||
id="mail.profiles-and-policy",
|
||||
title="Mail profiles and policy hierarchy",
|
||||
summary="Mail sending and mailbox access use reusable SMTP/IMAP/JMAP profiles governed by an effective system, tenant, owner, and campaign policy.",
|
||||
body="The active policy decides whether users can only choose approved profiles or whether user, group, and campaign scopes may define additional reusable profiles. SMTP, IMAP, and JMAP host allow/deny rules are inherited independently. JMAP Session discovery fails closed when its advertised API origin differs unless an administrator explicitly allows that origin. Runtime documentation adds the current tenant posture when the actor may read mail profile policy. In the Mail profile policy editor, Credential selection separately controls SMTP and IMAP: allow a profile default or require an explicit authorized Mail-owned server and credential reference in Campaign Mail settings. Both choices keep secrets in Mail. Inherit policy leaves the local choice unset and follows the parent; it is not the same as allowing default credentials. System values are concrete, while lower scopes show local, effective, and source-path values. Allow override sets the matching allow_lower_level_limits protocol key; an ancestor lock cannot be changed or re-enabled below that scope. Campaign policy has no lower-level override controls. Scope-write permission and an unlocked workflow remain required. A failed write preserves the draft for explicit retry; a successful write followed by a dependent refresh failure remains saved and asks only to reload the display.",
|
||||
body="Documentation books sit beside the Mailbox, legacy-import, bounce-processing, profile, and "
|
||||
"policy headings; field help stays with its label. "
|
||||
"The active policy decides whether users can only choose approved profiles or whether user, group, and campaign scopes may define additional reusable profiles. SMTP, IMAP, and JMAP host allow/deny rules are inherited independently. JMAP Session discovery fails closed when its advertised API origin differs unless an administrator explicitly allows that origin. Runtime documentation adds the current tenant posture when the actor may read mail profile policy. In the Mail profile policy editor, Credential selection separately controls SMTP and IMAP: allow a profile default or require an explicit authorized Mail-owned server and credential reference in Campaign Mail settings. Both choices keep secrets in Mail. Inherit policy leaves the local choice unset and follows the parent; it is not the same as allowing default credentials. System values are concrete, while lower scopes show local, effective, and source-path values. Allow override sets the matching allow_lower_level_limits protocol key; an ancestor lock cannot be changed or re-enabled below that scope. Campaign policy has no lower-level override controls. Scope-write permission and an unlocked workflow remain required. A failed write preserves the draft for explicit retry; a successful write followed by a dependent refresh failure remains saved and asks only to reload the display.",
|
||||
layer="configured",
|
||||
documentation_types=("admin",),
|
||||
audience=("tenant_admin", "mail_admin", "campaign_admin"),
|
||||
@@ -1102,6 +1104,8 @@ manifest = ModuleManifest(
|
||||
"title": "Mail-Profile und Richtlinienhierarchie",
|
||||
"summary": "Mail-Versand und Postfachzugriff über wiederverwendbare SMTP-/IMAP-/JMAP-Profile und eine wirksame Richtlinie aus System-, Mandanten-, Eigentümer- und gegebenenfalls Campaign-Ebene steuern.",
|
||||
"body": (
|
||||
"Dokumentationsbücher stehen neben den Überschriften für Postfach, Altimport, "
|
||||
"Rückläuferverarbeitung, Profile und Richtlinien; Feldhilfe bleibt bei der Feldbezeichnung. "
|
||||
"Die wirksame Richtlinie entscheidet, ob Personen ausschließlich freigegebene Profile auswählen dürfen "
|
||||
"oder ob auf Personen-, Gruppen- und Campaign-Ebene zusätzliche wiederverwendbare Profile angelegt werden können. "
|
||||
"Host-Freigaben und -Sperren für SMTP, IMAP und JMAP werden unabhängig vererbt. Ein von der JMAP-Session "
|
||||
@@ -1362,6 +1366,8 @@ manifest = ModuleManifest(
|
||||
title="Read a permitted mailbox without changing it",
|
||||
summary="Choose an IMAP- or JMAP-enabled profile, browse folders, search or page messages, and inspect bounded content through the read-only mailbox surface.",
|
||||
body=(
|
||||
"Documentation books sit beside the Mailbox, legacy-import, bounce-processing, profile, and "
|
||||
"policy headings; field help stays with its label. "
|
||||
"Mailbox access requires both mailbox-read and profile-use authority for a profile visible in the actor's scope. IMAP retains its existing bounded list behavior. IMAP folder names appear as readable Unicode, including umlauts and literal ampersands, and are encoded for the active connection when opened. Refresh an already-loaded folder list after an upgrade; folder discovery does not rename remote folders. Folder icons expand or collapse the tree; labels select without changing expansion. Selecting a synthetic parent grouping highlights that group and clears message selection without opening an invented provider folder. JMAP adds capability discovery, server-side text search, query-state cursors, and bounded Email/changes synchronization; an expired state explicitly requires a full refresh. Lists expose provider read/unread flags and provenance without mutating them. Message HTML is isolated and sanitized, while attachment metadata, unavailable content, and provider failures remain explicit. Listing folders or messages must not mark mail read, move it, delete it, or expose unbounded content. "
|
||||
"The persistent workspace header keeps the profile selector, Mailbox tools, Help, and one right-aligned Reload available even without a profile or message selection. Reload first rechecks authorized profiles, then refreshes the current folder catalogue and bounded message page together; a selected message is reread only if still present. IMAP retains the current page; JMAP starts a fresh cursor chain on page one while retaining the search. A selected synthetic grouping refreshes only the folder catalogue, not an invented mailbox. Folder expansion is retained. Failed refreshes preserve usable loaded data with an explicit error and retry action, never present failure as an empty mailbox, and ignore late reads from a previous profile or tenant. Mailbox tools groups the optional profile-only, folder-only, and message-only refreshes separately from Bounce status. The latter remains visible with a permission explanation when bounce-read/manage authority is absent. These controls do not grant profile administration rights, run SMTP delivery, append mail, or change read/unread flags. "
|
||||
"Mailbox context reads bypass browser response reuse; bounded server-side indexes and their provenance remain governed by Mail. A failed pagination request restores the page and size that belong to the retained rows. Dismissing or changing the preview while Reload is pending takes precedence over its remembered selection. On narrow screens, scroll vertically through folders, messages, and preview inside the mailbox workspace; its action header remains visible."
|
||||
@@ -1386,6 +1392,8 @@ manifest = ModuleManifest(
|
||||
"title": "Ein berechtigtes Postfach ohne Änderungen lesen",
|
||||
"summary": "Ein IMAP- oder JMAP-fähiges Profil auswählen, Ordner durchsuchen und begrenzte Nachrichteninhalte in der nur lesbaren Postfachoberfläche prüfen.",
|
||||
"body": (
|
||||
"Dokumentationsbücher stehen neben den Überschriften für Postfach, Altimport, "
|
||||
"Rückläuferverarbeitung, Profile und Richtlinien; Feldhilfe bleibt bei der Feldbezeichnung. "
|
||||
"Der Postfachzugriff erfordert sowohl Leseberechtigung für das Postfach als auch Nutzungsberechtigung für ein "
|
||||
"im Bereich der handelnden Person sichtbares Profil. IMAP zeigt lesbare Unicode-Ordnernamen mit Umlauten "
|
||||
"und kaufmännischen Und-Zeichen; beim Öffnen werden sie für die aktive Verbindung kodiert. Eine bereits "
|
||||
|
||||
@@ -41,7 +41,7 @@ assert.match(openFolderNode, /setSelectedFolderGroup\(\{ id: node\.id, label: no
|
||||
assert.match(openFolderNode, /messageDetailRequestRef\.current \+= 1/, "Selecting a grouping invalidates pending message detail.");
|
||||
assert.match(mailbox, /selectedFolderGroup\?\.id \?\? findFolderNodeId/);
|
||||
assert.match(mailbox, /<WorkspaceFrame[^>]*height="viewport"/);
|
||||
assert.match(mailbox, /<WorkspaceActionBar\s+variant="workspace"\s+scope="workspace"[\s\S]*refreshable[\s\S]*onReload: \(\) => void reloadMailbox\(\)/);
|
||||
assert.match(mailbox, /<WorkspaceActionBar\s+title="i18n:govoplan-mail\.mailbox_workspace"\s+titleHelp=\{<DocumentationHelpLink reference=\{MAILBOX_DOCUMENTATION\} \/>\}\s+variant="workspace"\s+scope="workspace"[\s\S]*refreshable[\s\S]*onReload: \(\) => void reloadMailbox\(\)/);
|
||||
assert.ok(mailbox.indexOf("<WorkspaceActionBar") < mailbox.indexOf('<aside className="file-tree-panel"'), "Mailbox actions remain outside all selectable panes.");
|
||||
assert.equal((mailbox.match(/<WorkspaceActionBar/g) ?? []).length, 1, "One persistent page-wide action bar.");
|
||||
assert.doesNotMatch(mailbox, /mailbox-toolbar|<ToolbarGroup/);
|
||||
|
||||
@@ -196,13 +196,13 @@ export default function MailBouncePage({ settings }: { settings: ApiSettings })
|
||||
<PageLayout
|
||||
archetype="collection"
|
||||
title="Bounce processing"
|
||||
titleHelp={<DocumentationHelpLink reference={MAIL_BOUNCE_DOCUMENTATION} />}
|
||||
description="Watch IMAP delivery-status folders and correlate recipient failures with Mail delivery commands."
|
||||
actions={<PageActionBar
|
||||
variant="collection"
|
||||
refreshable
|
||||
reloadAction={{ onReload: () => void load(), disabled: Boolean(pageMutationBlocker), disabledReason: pageMutationBlocker }}
|
||||
contextActions={<Button onClick={() => navigate("/mail")}><ArrowLeft size={16} aria-hidden="true" /> Mailbox</Button>}
|
||||
helpAction={<DocumentationHelpLink reference={MAIL_BOUNCE_DOCUMENTATION} />}
|
||||
createAction={<Button variant="primary" onClick={() => setAddOpen(true)} disabled={Boolean(addWatcherBlocker)} disabledReason={addWatcherBlocker}><Plus size={16} aria-hidden="true" /> Add watcher</Button>}
|
||||
/>}
|
||||
loading={loading}
|
||||
|
||||
@@ -379,6 +379,7 @@ export default function MailLegacyImportPage({
|
||||
<PageLayout
|
||||
archetype="collection"
|
||||
title="Legacy POP3 import"
|
||||
titleHelp={<DocumentationHelpLink reference={DOCUMENTATION} />}
|
||||
description="Migrate bounded messages into encrypted local review records. POP3 is disabled by default and is not recommended for ongoing mailbox access."
|
||||
helpContextId="mail.pop3"
|
||||
helpModuleId="mail"
|
||||
@@ -388,7 +389,6 @@ export default function MailLegacyImportPage({
|
||||
refreshable
|
||||
reloadAction={{ onReload: () => void load(), helpContextId: "mail.pop3.action.reload", helpModuleId: "mail", helpTopicId: "mail.workflow.legacy-pop3-import", disabled: Boolean(busy), disabledReason: busy ? "Wait for the current POP3 action to finish." : undefined }}
|
||||
contextActions={<Button onClick={() => navigate("/mail")}><ArrowLeft size={16} aria-hidden="true" /> Mailbox</Button>}
|
||||
helpAction={<DocumentationHelpLink reference={DOCUMENTATION} />}
|
||||
createAction={canManage ? <Button helpContextId="mail.pop3.action.create-source" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import" variant="primary" onClick={() => openSourceDialog(null)} disabled={Boolean(busy) || configurableProfiles.length === 0} disabledReason={configurableProfiles.length === 0 ? "Create or gain write access to a Mail profile first." : undefined}><Plus size={16} aria-hidden="true" /> Add legacy source</Button> : undefined}
|
||||
/>}
|
||||
loading={loading}
|
||||
|
||||
@@ -630,9 +630,9 @@ export function MailProfileScopeManager({
|
||||
<>
|
||||
<Card
|
||||
title={profileTitle}
|
||||
titleHelp={<DocumentationHelpLink reference={MAIL_PROFILE_DOCUMENTATION} />}
|
||||
actions={
|
||||
<div className="button-row compact-actions">
|
||||
<DocumentationHelpLink reference={MAIL_PROFILE_DOCUMENTATION} />
|
||||
<Button onClick={() => void loadProfiles()} disabled={Boolean(profileReloadBlocker)} disabledReason={profileReloadBlocker}>{loading ? "i18n:govoplan-mail.loading.33ce4174" : "i18n:govoplan-mail.reload.cce71553"}</Button>
|
||||
<Button variant="primary" onClick={openCreate} disabled={Boolean(profileMutationBlocker)} disabledReason={profileMutationBlocker}><Plus size={16} />i18n:govoplan-mail.new_profile.ca36da25</Button>
|
||||
</div>
|
||||
|
||||
@@ -224,9 +224,9 @@ export function MailProfilePolicyEditor({
|
||||
return (
|
||||
<Card
|
||||
title={title}
|
||||
titleHelp={<DocumentationHelpLink reference={MAIL_PROFILE_DOCUMENTATION} />}
|
||||
actions={
|
||||
<div className="button-row compact-actions">
|
||||
<DocumentationHelpLink reference={MAIL_PROFILE_DOCUMENTATION} />
|
||||
<Button onClick={() => void loadPolicy()} disabled={loading || busy || !scopeReady} disabledReason={loading ? "Mail policy is already loading." : busy ? "Wait for the current policy change to finish." : !scopeReady ? "Select a policy target before reloading." : undefined}>{loading ? "i18n:govoplan-mail.loading.33ce4174" : "i18n:govoplan-mail.reload.cce71553"}</Button>
|
||||
<Button variant="primary" onClick={() => void savePolicy()} disabled={Boolean(policySaveBlocker)} disabledReason={policySaveBlocker}>{busy ? "i18n:govoplan-mail.saving.56a2285c" : "i18n:govoplan-mail.save_policy.77d67ce3"}</Button>
|
||||
</div>
|
||||
|
||||
@@ -626,6 +626,8 @@ export default function MailboxPage({ settings, auth }: { settings: ApiSettings;
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
|
||||
<WorkspaceActionBar
|
||||
title="i18n:govoplan-mail.mailbox_workspace"
|
||||
titleHelp={<DocumentationHelpLink reference={MAILBOX_DOCUMENTATION} />}
|
||||
variant="workspace"
|
||||
scope="workspace"
|
||||
label="i18n:govoplan-mail.mail_actions.c08b5f08"
|
||||
@@ -647,7 +649,6 @@ export default function MailboxPage({ settings, auth }: { settings: ApiSettings;
|
||||
</FormField>
|
||||
<Button type="button" onClick={() => setMailToolsOpen(true)} helpContextId="mail.mailbox.tools" helpModuleId="mail" helpTopicId="mail.workflow.read-mailbox">i18n:govoplan-mail.mailbox_tools</Button>
|
||||
</>}
|
||||
helpAction={<DocumentationHelpLink reference={MAILBOX_DOCUMENTATION} />}
|
||||
/>
|
||||
|
||||
<div className={`file-manager-shell mailbox-shell ${shellBusy ? "is-loading" : ""}`}>
|
||||
|
||||
Reference in New Issue
Block a user