feat: add powered-off alert toggle for guests

Addresses #485 - adds UI controls for disabling powered-off alerts on a per-guest basis.

Changes:
- Add "Alert Powered-Off" / "No Powered-Off" toggle button for VMs/LXCs
- Extend toggleNodeConnectivity() to handle guests in addition to nodes/PBS
- Add disableConnectivity field to guest resource mapping
- Update hasOverride logic to track connectivity state

Previously, users could only disable ALL alerts for a guest or none.
Now they can independently control resource metric alerts vs powered-off alerts,
matching the functionality already available for nodes and PBS servers.

User impact:
- Enabled + Alert Powered-Off: All alerts including power state (default)
- Enabled + No Powered-Off: Only resource alerts, ignore power state
- Disabled: No alerts at all

Backend already supports this via DisableConnectivity flag.
This commit is contained in:
rcourtman
2025-10-01 20:56:44 +00:00
parent 346cb19da6
commit bd658c33fa
2 changed files with 40 additions and 5 deletions
@@ -311,6 +311,22 @@ export function ResourceTable(props: ResourceTableProps) {
{resource.disabled ? 'Disabled' : 'Enabled'}
</button>
</Show>
<Show
when={resource.type === 'guest' && props.onToggleNodeConnectivity}
>
<button
type="button"
onClick={() => props.onToggleNodeConnectivity?.(resource.id)}
class={`px-2 py-0.5 text-xs font-medium rounded transition-colors ${
resource.disableConnectivity
? 'bg-red-100 dark:bg-red-900/50 text-red-700 dark:text-red-300 hover:bg-red-200 dark:hover:bg-red-800/50'
: 'bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300 hover:bg-green-200 dark:hover:bg-green-800/50'
}`}
title="Toggle powered-off alerts for this guest"
>
{resource.disableConnectivity ? 'No Powered-Off' : 'Alert Powered-Off'}
</button>
</Show>
<Show
when={resource.type === 'node' && props.onToggleNodeConnectivity}
>
@@ -680,6 +696,22 @@ export function ResourceTable(props: ResourceTableProps) {
{resource.disabled ? 'Disabled' : 'Enabled'}
</button>
</Show>
<Show
when={resource.type === 'guest' && props.onToggleNodeConnectivity}
>
<button
type="button"
onClick={() => props.onToggleNodeConnectivity?.(resource.id)}
class={`px-2 py-0.5 text-xs font-medium rounded transition-colors ${
resource.disableConnectivity
? 'bg-red-100 dark:bg-red-900/50 text-red-700 dark:text-red-300 hover:bg-red-200 dark:hover:bg-red-800/50'
: 'bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300 hover:bg-green-200 dark:hover:bg-green-800/50'
}`}
title="Toggle powered-off alerts for this guest"
>
{resource.disableConnectivity ? 'No Powered-Off' : 'Alert Powered-Off'}
</button>
</Show>
<Show when={resource.type === 'node' && props.onToggleNodeConnectivity}>
<button
type="button"
@@ -225,8 +225,9 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
);
});
// A guest has an override if it has custom thresholds OR is disabled
const hasOverride = hasCustomThresholds || override?.disabled || false;
// A guest has an override if it has custom thresholds OR is disabled OR has connectivity disabled
const hasOverride =
hasCustomThresholds || override?.disabled || override?.disableConnectivity || false;
return {
id: guestId,
@@ -239,6 +240,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
status: guest.status,
hasOverride: hasOverride,
disabled: override?.disabled || false,
disableConnectivity: override?.disableConnectivity || false,
thresholds: override?.thresholds || {},
defaults: props.guestDefaults,
};
@@ -573,11 +575,12 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
};
const toggleNodeConnectivity = (resourceId: string, forceState?: boolean) => {
// Find the resource - could be a node or PBS server
// Find the resource - could be a node, PBS server, or guest
const nodes = nodesWithOverrides();
const pbsServers = pbsServersWithOverrides();
const resource = [...nodes, ...pbsServers].find((r) => r.id === resourceId);
if (!resource || (resource.type !== 'node' && resource.type !== 'pbs')) return;
const guests = guestsWithOverrides();
const resource = [...nodes, ...pbsServers, ...guests].find((r) => r.id === resourceId);
if (!resource || (resource.type !== 'node' && resource.type !== 'pbs' && resource.type !== 'guest')) return;
// Get existing override if it exists
const existingOverride = props.overrides().find((o) => o.id === resourceId);