fleet-manager ui: target version as a dropdown (fleet-known + custom)

Replace the free-text target-version field with a select populated from versions
known across the fleet (each host's running + targeted version, newest first),
plus a 'custom...' escape to a text input for a version not yet seen. Avoids
typos on the pin. (Authoritative serving-side source is a follow-up.)
This commit is contained in:
Audiobuzz
2026-07-04 06:08:15 +00:00
parent 20b29d9990
commit f71a55eab5
@@ -11,8 +11,30 @@
let busy = $state(false)
let confirmOpen = $state(false)
let rebootMode = $state('none') // none | now | at
let customVersion = $state(false)
let form = $state({ target_version: '', kiosk_url: '', reboot_at: '', drain: false })
// Versions known across the fleet (running + already-targeted), newest first.
// A "custom…" escape covers a version not yet seen anywhere. (Future: source
// from the serving-side release inventory / deployment manifest.)
let versions = $derived.by(() => {
const s = new Set()
for (const h of hosts) {
if (h.version?.image_version) s.add(h.version.image_version)
if (h.desired?.target_version) s.add(h.desired.target_version)
}
return [...s].sort((a, b) => b.localeCompare(a, undefined, { numeric: true }))
})
function onVersionSelect(e) {
const v = e.target.value
if (v === '__custom__') {
customVersion = true
form.target_version = ''
} else {
form.target_version = v
}
}
async function load() {
try {
const d = await api.get('/api/hosts')
@@ -26,6 +48,7 @@
selected = h
okMsg = ''
rebootMode = 'none'
customVersion = false
form = {
target_version: h.desired?.target_version || '',
kiosk_url: h.desired?.kiosk_url || '',
@@ -90,7 +113,18 @@
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
<label class="block">
<span class="tag">target version</span>
<input class="mt-1 w-full rounded border border-line bg-void2 px-2 py-1.5 text-sm text-ink" bind:value={form.target_version} placeholder="e.g. 0.5.0" />
{#if customVersion || !versions.length}
<div class="mt-1 flex items-center gap-2">
<input class="min-w-0 flex-1 rounded border border-line bg-void2 px-2 py-1.5 text-sm text-ink" bind:value={form.target_version} placeholder="e.g. 0.5.0" />
{#if versions.length}<button type="button" class="btn" onclick={() => (customVersion = false)}>list</button>{/if}
</div>
{:else}
<select class="mt-1 w-full rounded border border-line bg-void2 px-2 py-1.5 text-sm text-ink" value={form.target_version} onchange={onVersionSelect}>
<option value="">— unset —</option>
{#each versions as v}<option value={v}>{v}</option>{/each}
<option value="__custom__">custom…</option>
</select>
{/if}
</label>
<label class="block">
<span class="tag">kiosk url</span>