mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-22 03:02:35 +00:00
feat: add batch toggle for alerts in Thresholds tab
addresses #349 - adds a toggle button in the Alerts column header that allows users to enable/disable all alerts for a resource type with one click. This is especially helpful when managing many VMs, containers, or storage devices. - added toggle icon in Alerts column header for VMs & Containers, Storage, and Nodes tables - icon shows current state (eye for enabled, eye-slash for disabled) - clicking toggles all resources in that table between enabled/disabled - for nodes, toggles connectivity alerts instead of general disable flag
This commit is contained in:
parent
7592c95021
commit
3ead28bc4f
2 changed files with 71 additions and 5 deletions
|
|
@ -13,6 +13,7 @@ interface ResourceTableProps {
|
|||
onRemoveOverride: (resourceId: string) => void;
|
||||
onToggleDisabled?: (resourceId: string) => void;
|
||||
onToggleNodeConnectivity?: (nodeId: string) => void;
|
||||
onBatchToggleAlerts?: (resourceIds: string[], enable: boolean) => void;
|
||||
editingId: () => string | null;
|
||||
editingThresholds: () => Record<string, any>;
|
||||
setEditingThresholds: (value: Record<string, any>) => void;
|
||||
|
|
@ -21,6 +22,28 @@ interface ResourceTableProps {
|
|||
}
|
||||
|
||||
export function ResourceTable(props: ResourceTableProps) {
|
||||
// Get all resource IDs for batch operations
|
||||
const getAllResourceIds = () => {
|
||||
if (props.groupedResources) {
|
||||
return Object.values(props.groupedResources).flat().map(r => r.id);
|
||||
}
|
||||
return props.resources?.map(r => r.id) || [];
|
||||
};
|
||||
|
||||
// Check if all alerts are disabled
|
||||
const areAllAlertsDisabled = () => {
|
||||
const resources = props.groupedResources ? Object.values(props.groupedResources).flat() : (props.resources || []);
|
||||
if (resources.length === 0) return false;
|
||||
|
||||
// For nodes, check disableConnectivity instead of disabled
|
||||
if (props.title === 'Proxmox Nodes') {
|
||||
return resources.every(r => r.disableConnectivity === true);
|
||||
}
|
||||
|
||||
// For guests and storage, check disabled flag
|
||||
return resources.every(r => r.disabled === true);
|
||||
};
|
||||
|
||||
const MetricValueWithHeat = (metricProps: {
|
||||
resourceId: string;
|
||||
metric: string;
|
||||
|
|
@ -70,7 +93,50 @@ export function ResourceTable(props: ResourceTableProps) {
|
|||
)}
|
||||
</For>
|
||||
<th class="px-3 py-2 text-center text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Alerts
|
||||
<div class="flex items-center justify-center gap-1">
|
||||
<span>Alerts</span>
|
||||
<Show when={(props.title === 'VMs & Containers' || props.title === 'Storage Devices' || props.title === 'Proxmox Nodes') && getAllResourceIds().length > 0}>
|
||||
<button type="button"
|
||||
onClick={() => {
|
||||
const allDisabled = areAllAlertsDisabled();
|
||||
const resourceIds = getAllResourceIds();
|
||||
if (props.title === 'Proxmox Nodes' && props.onToggleNodeConnectivity) {
|
||||
// For nodes, toggle connectivity alerts
|
||||
resourceIds.forEach(id => {
|
||||
const resource = props.resources?.find(r => r.id === id);
|
||||
if (resource && (allDisabled || !resource.disableConnectivity)) {
|
||||
props.onToggleNodeConnectivity!(id);
|
||||
}
|
||||
});
|
||||
} else if (props.onToggleDisabled) {
|
||||
// For guests and storage, toggle disabled flag
|
||||
resourceIds.forEach(id => {
|
||||
const resources = props.groupedResources ? Object.values(props.groupedResources).flat() : (props.resources || []);
|
||||
const resource = resources.find(r => r.id === id);
|
||||
if (resource && (allDisabled || !resource.disabled)) {
|
||||
props.onToggleDisabled!(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
class={`p-0.5 rounded transition-colors ${
|
||||
areAllAlertsDisabled()
|
||||
? 'text-red-600 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-900/50'
|
||||
: 'text-green-600 dark:text-green-400 hover:bg-green-100 dark:hover:bg-green-900/50'
|
||||
}`}
|
||||
title={areAllAlertsDisabled() ? 'Enable all alerts' : 'Disable all alerts'}
|
||||
>
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<Show when={areAllAlertsDisabled()} fallback={
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
|
||||
}>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</Show>
|
||||
</svg>
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
</th>
|
||||
<th class="px-3 py-2 text-center text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Actions
|
||||
|
|
|
|||
|
|
@ -425,7 +425,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
|
|||
props.setHasUnsavedChanges(true);
|
||||
};
|
||||
|
||||
const toggleDisabled = (resourceId: string) => {
|
||||
const toggleDisabled = (resourceId: string, forceState?: boolean) => {
|
||||
// Flatten grouped guests to find the resource
|
||||
const allGuests = Object.values(guestsGroupedByNode()).flat();
|
||||
const allResources = [...allGuests, ...storageWithOverrides()];
|
||||
|
|
@ -437,7 +437,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
|
|||
|
||||
// Determine the current disabled state - check the resource's current state, not the override
|
||||
const currentDisabledState = resource.disabled;
|
||||
const newDisabledState = !currentDisabledState;
|
||||
const newDisabledState = forceState !== undefined ? forceState : !currentDisabledState;
|
||||
|
||||
// Clean the thresholds to exclude 'disabled' if it got in there
|
||||
const cleanThresholds: any = { ...(existingOverride?.thresholds || {}) };
|
||||
|
|
@ -500,7 +500,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
|
|||
props.setHasUnsavedChanges(true);
|
||||
};
|
||||
|
||||
const toggleNodeConnectivity = (nodeId: string) => {
|
||||
const toggleNodeConnectivity = (nodeId: string, forceState?: boolean) => {
|
||||
const node = nodesWithOverrides().find(r => r.id === nodeId);
|
||||
if (!node || node.type !== 'node') return;
|
||||
|
||||
|
|
@ -509,7 +509,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
|
|||
|
||||
// Determine the current state
|
||||
const currentDisableConnectivity = existingOverride?.disableConnectivity || false;
|
||||
const newDisableConnectivity = !currentDisableConnectivity;
|
||||
const newDisableConnectivity = forceState !== undefined ? forceState : !currentDisableConnectivity;
|
||||
|
||||
// Clean the thresholds to exclude any unwanted fields
|
||||
const cleanThresholds: any = { ...(existingOverride?.thresholds || {}) };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue