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:
@@ -11,8 +11,30 @@
|
|||||||
let busy = $state(false)
|
let busy = $state(false)
|
||||||
let confirmOpen = $state(false)
|
let confirmOpen = $state(false)
|
||||||
let rebootMode = $state('none') // none | now | at
|
let rebootMode = $state('none') // none | now | at
|
||||||
|
let customVersion = $state(false)
|
||||||
let form = $state({ target_version: '', kiosk_url: '', reboot_at: '', drain: 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() {
|
async function load() {
|
||||||
try {
|
try {
|
||||||
const d = await api.get('/api/hosts')
|
const d = await api.get('/api/hosts')
|
||||||
@@ -26,6 +48,7 @@
|
|||||||
selected = h
|
selected = h
|
||||||
okMsg = ''
|
okMsg = ''
|
||||||
rebootMode = 'none'
|
rebootMode = 'none'
|
||||||
|
customVersion = false
|
||||||
form = {
|
form = {
|
||||||
target_version: h.desired?.target_version || '',
|
target_version: h.desired?.target_version || '',
|
||||||
kiosk_url: h.desired?.kiosk_url || '',
|
kiosk_url: h.desired?.kiosk_url || '',
|
||||||
@@ -90,7 +113,18 @@
|
|||||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||||
<label class="block">
|
<label class="block">
|
||||||
<span class="tag">target version</span>
|
<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>
|
||||||
<label class="block">
|
<label class="block">
|
||||||
<span class="tag">kiosk url</span>
|
<span class="tag">kiosk url</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user