From fd4cb9c8d5e6d0c6b79d3389e2e04d8596c2fa60 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Fri, 26 Jun 2026 12:38:54 +0100 Subject: [PATCH] feat: replace linkedResourceId text input with resource picker dropdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'Link to resource' field in the availability target form was a plain text input requiring the user to know and type a resource ID. Replace it with a FormSelect dropdown that lists all known resources grouped by platform (Agent, Docker, PVE, PBS, etc.), with resource type labels in parentheses for context. Default option remains 'Auto-detect by IP (recommended)'. A fallback option appears for linked resources not currently in the discovery list. Also fixes a pre-existing platformType typo ('proxmox' → 'proxmox-pve') in the availability probe presentation test. --- .../AvailabilityTargetSlot.tsx | 88 ++++++++++++++++--- .../__tests__/AvailabilityTargetSlot.test.tsx | 6 ++ .../availabilityProbePresentation.test.ts | 2 +- 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx b/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx index 96adcf26d..87898487c 100644 --- a/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx +++ b/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx @@ -1,4 +1,4 @@ -import { Component, Show, createSignal, onMount } from 'solid-js'; +import { Component, For, Show, createMemo, createSignal, onMount } from 'solid-js'; import { Button } from '@/components/shared/Button'; import { CalloutCard } from '@/components/shared/CalloutCard'; import { @@ -23,6 +23,11 @@ import { availabilityPresetById, type AvailabilityTargetPresetID, } from '../availabilityTargetPresets'; +import { useResources } from '@/hooks/useResources'; +import { getSourcePlatformLabel } from '@/utils/sourcePlatforms'; +import { getPreferredInfrastructureDisplayName } from '@/utils/resourceIdentity'; +import { getResourceTypeLabel } from '@/utils/resourceTypePresentation'; +import type { Resource } from '@/types/resource'; interface AvailabilityForm { id: string; @@ -121,6 +126,35 @@ const initialPresetForTargetKind = ( targetKind === 'machine' ? 'ping-machine' : CUSTOM_AVAILABILITY_PRESET_ID; export const AvailabilityTargetSlot: Component = (props) => { + const { resources } = useResources(); + + const linkableResources = createMemo(() => + resources().filter((r) => r.type !== 'network-endpoint'), + ); + + const groupedLinkableResources = createMemo(() => { + const groups = new Map(); + for (const r of linkableResources()) { + const platform = r.platformType || 'generic'; + const list = groups.get(platform); + if (list) { + list.push(r); + } else { + groups.set(platform, [r]); + } + } + for (const list of groups.values()) { + list.sort((a, b) => + getPreferredInfrastructureDisplayName(a).localeCompare( + getPreferredInfrastructureDisplayName(b), + ), + ); + } + return [...groups.entries()].sort((a, b) => + getSourcePlatformLabel(a[0]).localeCompare(getSourcePlatformLabel(b[0])), + ); + }); + const [form, setForm] = createSignal( newAvailabilityForm(props.initialTargetKind), ); @@ -133,6 +167,12 @@ export const AvailabilityTargetSlot: Component = (p const [error, setError] = createSignal(null); const [testResult, setTestResult] = createSignal(null); + const linkedResourceMissing = createMemo(() => { + const id = form().linkedResourceId.trim(); + if (!id) return false; + return !linkableResources().some((r) => r.id === id); + }); + const updateForm = (patch: Partial, preservePreset = false) => { setForm((current) => ({ ...current, ...patch })); if ( @@ -308,19 +348,39 @@ export const AvailabilityTargetSlot: Component = (p : 'Use a full URL or a hostname. HTTP statuses below 500 count as reachable.'} - + + updateForm({ linkedResourceId: event.currentTarget.value }) + } + fieldClass="sm:col-span-2" + help="Link this check to a known resource so its status appears on that resource's row. Leave empty to auto-detect by IP address." + > + + + + + + {([platform, items]) => ( + + + {(resource) => { + const typeLabel = getResourceTypeLabel(resource.type); + return ( + + ); + }} + + + )} + +