import { useCallback, useEffect, useRef, useState } from 'react'; import { Link, useSearchParams } from 'react-router'; import { Settings } from 'lucide-react'; import { useClient } from 'hooks/useClient'; import { DEFAULT_SECTION, isSectionKey, sectionFor, type SectionKey } from './sections'; import { ConnectionPanel } from './ConnectionPanel'; import { MessagesPanel } from './MessagesPanel'; import { PlannedSection } from './PlannedSection'; import { useFofosChanged } from './channel'; /** * The right column: whichever section the nav has selected. * * It reads `?section=` from the URL rather than being handed it — the two panels are siblings in a * workspace and neither owns the other. `?config=1` renders the connection form over the top of * whatever is selected, so settings are addressable too. * * ── The unconfigured check lives HERE, once ── * * Every section needs a connection, so every section would otherwise have to handle its absence and * each would word it slightly differently. Asking `/status` once and rendering the connection form * instead of any section is one answer for all nine, and it makes "not connected yet" read as a setup * step rather than as nine broken lists. */ type Status = { configured: boolean; lastProbeOk: boolean | null }; /** * Which component renders which section. * * A map rather than a conditional on `built`. The version of this that read * `built ? : ` was correct only while exactly one section existed — the * second one to be finished would have rendered Messages under its own name, which is the kind of bug * that looks like a data problem for an hour. A key with no entry here falls through to Planned, so * marking a section `built` in sections.ts without writing its panel fails visibly rather than * silently showing somebody else's. */ const PANELS: Partial React.JSX.Element>> = { messages: MessagesPanel, }; export const DetailPanel = () => { const [params] = useSearchParams(); const raw = params.get('section'); const section: SectionKey = isSectionKey(raw) ? raw : DEFAULT_SECTION; const showingConfig = params.get('config') === '1'; const client = useClient(); const clientRef = useRef(client); clientRef.current = client; const [configured, setConfigured] = useState(null); const [signal] = useFofosChanged(); const check = useCallback(async () => { try { const status = await clientRef.current.get('/fofos-admin/status'); setConfigured(status.configured); } catch { // Cannot tell. Render the section rather than the setup form — a transient failure must not look // like "you have not set this up", which is a much more alarming thing to be told, and would // invite somebody to re-enter a connection that was never the problem. setConfigured(true); } }, []); useEffect(() => { void check(); }, [check, signal]); if (configured === null) { return
Loading…
; } if (showingConfig || !configured) return ; const current = sectionFor(section); // Everything but `config`, so the settings link keeps the section underneath it. const configSearch = new URLSearchParams(params); configSearch.set('config', '1'); const Panel = current.built ? PANELS[current.key] : undefined; return (

{current.label}

{/* A real link, so the settings are addressable and cmd-clickable like anything else. */} Connection
{Panel ? : }
); };