diff --git a/src/components/forms/DynamicForm.tsx b/src/components/forms/DynamicForm.tsx
index a27233f..5e453fa 100644
--- a/src/components/forms/DynamicForm.tsx
+++ b/src/components/forms/DynamicForm.tsx
@@ -13,7 +13,7 @@ import { HelpPanel } from '@/help/HelpPanel';
import { fieldPlaceholder, formNotices, variantPrefill } from '@/features/ai/formExtras';
import { FailedRecipients } from '@/features/ai/explain/FailedRecipients';
import { iconForView } from '@/lib/viewIcon';
-import { useState, useEffect, useCallback, useMemo } from 'react';
+import { useState, useEffect, useCallback, useMemo, type ReactNode } from 'react';
import { flushSync } from 'react-dom';
import { useNavigate, useBlocker } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
@@ -40,7 +40,8 @@ import {
DialogFooter,
} from '@/components/ui/dialog';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
-import { ArrowLeft, Save, Trash2, Loader2 } from 'lucide-react';
+import { ArrowLeft, ChevronDown, Save, Trash2, Loader2 } from 'lucide-react';
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import { AccountLockButton } from '@/features/lock/AccountLockButton';
import { useSchemaStore } from '@/stores/schemaStore';
@@ -73,9 +74,13 @@ import type { JmapSetResponse, JmapSetError, JmapMethodCall } from '@/types/jmap
interface DynamicFormProps {
viewName: string;
objectId: string | null;
+ /** inbuxa: a summary drawn right under the page heading, above the fields (settings-reorg). */
+ intro?: ReactNode;
+ /** inbuxa: fold the fields and Save under this label, for pages whose intro does the everyday work. */
+ foldSections?: string;
}
-export function DynamicForm({ viewName, objectId }: DynamicFormProps) {
+export function DynamicForm({ viewName, objectId, intro, foldSections }: DynamicFormProps) {
const { t } = useTranslation();
const navigate = useNavigate();
const schema = useSchemaStore((s) => s.schema);
@@ -790,6 +795,8 @@ export function DynamicForm({ viewName, objectId }: DynamicFormProps) {
actions={
{generalError}
@@ -816,136 +823,138 @@ export function DynamicForm({ viewName, objectId }: DynamicFormProps) {{t('enterprise.featureDisabled', "This feature isn't available on this server.")}
+{t('enterprise.featureDisabled', "This feature isn't available on this server.")}
-{t('spamLevels.title', 'How hard the spam filter is')}
+ {current === null && ( + + {t('spamLevels.byHand', 'Thresholds set by hand below')} + + )} + {changed && ( ++ {t( + 'spamLevels.discardWarning', + 'A discard threshold is set by hand. Picking a level turns it off: deleted mail can’t be found again, and nobody is told.', + )} +
+ )} + ++ {t('spamLevels.more', 'More:')} {link('CustomComponent/LocalAi', t('spamLevels.localAi', 'Local AI'))} + {' · '} + {link('x:SpamDnsblServer', t('spamLevels.blocklists', 'Blocklists'))} + {' · '} + {link('x:SpamTag', t('spamLevels.scores', 'Scores'))} + {' · '} + {link('x:MemoryLookupKey/SpamTrustedDomain', t('spamLevels.trusted', 'Trusted domains'))} +
+ > + )} +