import { useEffect, useMemo, useState } from 'react' import { Sensor, fetchCatalog, fmtValue } from './sensors' import { apiPath } from './api' import { WIDGET_TYPES, WidgetSpec, WidgetType } from './widgets' import { Btn, Loading, useEscape } from './ui' // Pick a sensor from this machine's catalog (or type a pattern) and how to draw it. export default function WidgetPicker({ onAdd, onClose, initial }: { onAdd: (w: WidgetSpec) => void; onClose: () => void; initial?: WidgetSpec }) { const [cat, setCat] = useState(null) const [err, setErr] = useState('') const [filter, setFilter] = useState('') const [sensor, setSensor] = useState(initial?.sensor ?? '') const [type, setType] = useState(initial?.type ?? 'stat') const [title, setTitle] = useState(initial?.title ?? '') const [bad, setBad] = useState('') // One bad pattern would 400 the stream every widget on the page shares, so ask the API first. const add = async () => { try { const r = await fetch(apiPath('/api/sensors/query'), { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ w: sensor }) }) if (!r.ok) { setBad((await r.json().catch(() => ({}))).error ?? `the API refused it (${r.status})`); return } } catch { setBad('could not reach the API'); return } onAdd({ id: initial?.id ?? 'w' + Date.now().toString(36), type: pattern ? 'list' : type === 'list' ? 'stat' : type, sensor, title: title.trim() || undefined }) } useEffect(() => { fetchCatalog().then(setCat).catch((e) => setErr(e instanceof Error ? e.message : 'failed')) }, []) const shown = useMemo(() => { const f = filter.trim().toLowerCase() return (cat ?? []).filter((s) => !f || s.id.includes(f) || s.name.toLowerCase().includes(f)) }, [cat, filter]) useEscape(onClose) const pattern = /[*?[]/.test(sensor) const pick = (id: string) => { setSensor(id); setBad(''); if (/[*?[]/.test(id)) setType('list') } return (
e.stopPropagation()}>

{initial ? 'Edit widget' : 'Add a widget'}

setFilter(e.target.value)} placeholder="filter sensors — cpu, temp, fan, enp3s0…" /> {err &&
{err}
} {!cat && !err && } {cat && (
{shown.map((s) => ( ))} {shown.length === 0 &&
No sensor matches.
}
)}
{WIDGET_TYPES.filter((t) => !!t.many === pattern).map((t) => )}
{bad &&
{bad}
}
{initial ? 'Save' : 'Add'}
) }