Help on every option and every page

- Each option's explanation moves from a line of text under its label to
  a small tooltip on an ⓘ beside it, so forms read calmer and the help is
  still one hover away.
- Help text is ours where written (src/help/texts.ts: domains, people,
  DNS providers, blocked addresses, and the main pages), and the schema's
  description elsewhere.
- A "?" on every list and form opens a side panel: what the page is for,
  what people usually do there, and every option explained.
- Every tooltip and panel carries a stable help id (x:Domain.dnsManagement,
  x:Domain), and manual.ts turns an id into a link to the admin manual
  once one is configured (VITE_MANUAL_URL, or <meta name="manual-url">).
  Until then no link shows.
This commit is contained in:
2026-09-19 01:59:14 -07:00
parent 7ab0b8099c
commit 824427ef46
12 changed files with 512 additions and 18 deletions
+12
View File
@@ -7,6 +7,7 @@
import { humanize } from '@/lib/humanize';
import { PageHeader } from '@/components/common/PageHeader';
import { HelpPanel } from '@/help/HelpPanel';
import { iconForView } from '@/lib/viewIcon';
import { useState, useEffect, useCallback, useMemo } from 'react';
import { flushSync } from 'react-dom';
@@ -124,6 +125,15 @@ export function DynamicForm({ viewName, objectId }: DynamicFormProps) {
return { ...fields, properties: filtered };
}, [resolved, selectedVariant, schema]);
// INBUXA: whose fields these are, for their help ids: the variant's schema
// (x:UserAccount) when the object has variants, else the object (x:Domain).
const helpScope = useMemo(() => {
if (!resolved) return undefined;
const { sch, obj } = resolved;
if (sch.type === 'single') return obj.objectName;
return sch.variants.find((v) => v.name === selectedVariant)?.schemaName ?? obj.objectName;
}, [resolved, selectedVariant]);
const currentForm = useMemo((): Form | null => {
if (!schema || !resolved) return null;
const { obj, sch } = resolved;
@@ -761,6 +771,7 @@ export function DynamicForm({ viewName, objectId }: DynamicFormProps) {
icon={iconForView(schema, viewName)}
title={formTitle}
subtitle={formSubtitle}
actions={<HelpPanel viewName={viewName} title={String(formTitle ?? '')} />}
/>
{generalError && (
@@ -834,6 +845,7 @@ export function DynamicForm({ viewName, objectId }: DynamicFormProps) {
sieveScriptName={
isSieveScriptField(resolved.obj.objectName, formField.name) ? scriptName : undefined
}
helpScope={helpScope}
/>
);