diff --git a/client/src/routes/admin/views/EventEditor.jsx b/client/src/routes/admin/views/EventEditor.jsx index ef6fdea..f48bc25 100644 --- a/client/src/routes/admin/views/EventEditor.jsx +++ b/client/src/routes/admin/views/EventEditor.jsx @@ -220,6 +220,46 @@ function SearchableOptions({ sourceId, label, disabled, onPick }) { ) } +/** + * The chosen option's note (MODULE_API 1.13.0), found even when the list the + * form holds does not reach it. + * + * A searchable source answers the form a bounded first page, so the value an + * author picked — or a template filled in — may not be in it, and its note with + * it. Then the source is asked for that value as a search term, once per value, + * and the row whose value matches exactly is the one whose note is shown. A + * failure is no note: advice never breaks the field it advises. + */ +function useChosenNote(sourceId, value, entry) { + const [asked, setAsked] = useState({ key: null, note: null }) + const text = value === undefined || value === null ? '' : String(value) + const known = noteFor(entry, value) + const needAsk = Boolean(sourceId && entry?.state === 'ok' && entry.searchable && text && !known && + !entry.options.some((o) => o.value === text)) + const key = needAsk ? `${sourceId}\u0000${text}` : null + + useEffect(() => { + if (!key || asked.key === key) return undefined + let alive = true + const timer = setTimeout(async () => { + try { + const answer = await api.admin.eventOptions(sourceId, text) + const row = answer?.ok ? (answer.options || []).find((o) => o.value === text) : null + if (alive) setAsked({ key, note: row?.note || null }) + } catch { + if (alive) setAsked({ key, note: null }) + } + }, 300) + return () => { + alive = false + clearTimeout(timer) + } + }, [key, asked.key, sourceId, text]) + + if (known) return known + return key && asked.key === key ? asked.note : null +} + /** * A row of buttons for a choice of two or three (Rust PLAN_REDESIGNS §2.4). * @@ -366,7 +406,7 @@ function ParamField({ param, value, sourceEntry, disabled, onChange, missing, la // editable rather than look unset. const segmented = param.source && param.type !== 'boolean' && !values && isSegmentable(sourceEntry) const offList = segmented && asText !== '' && !sourceEntry.options.some((o) => o.value === asText) - const note = param.source ? noteFor(sourceEntry, value) : null + const note = useChosenNote(param.source, value, sourceEntry) return (