Refactor cards to be generic and reusable
This commit is contained in:
parent
08f00d9c62
commit
3d13a05c0f
@ -19,101 +19,53 @@
|
|||||||
<div v-show="sections.info" class="section-content-wrapper">
|
<div v-show="sections.info" class="section-content-wrapper">
|
||||||
<div class="section-content">
|
<div class="section-content">
|
||||||
<div class="metrics-row">
|
<div class="metrics-row">
|
||||||
<!-- Card 1: Cluster Name -->
|
<!-- Card 1: PVC Version -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="PVC Version"
|
||||||
<h6 class="card-title mb-0">
|
:value="clusterData.pvc_version || 'Unknown'"
|
||||||
<span class="metric-label">Cluster</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ clusterData.cluster_name || 'Unknown' }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 2: Version -->
|
<!-- Card 2: Primary Node -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Primary Node"
|
||||||
<h6 class="card-title mb-0">
|
:value="clusterData.primary_node || 'N/A'"
|
||||||
<span class="metric-label">Version</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ clusterData.pvc_version || 'Unknown' }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 4: Nodes -->
|
<!-- Card 3: Nodes -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Nodes"
|
||||||
<h6 class="card-title mb-0">
|
:value="clusterData.nodes?.total || 0"
|
||||||
<span class="metric-label">Nodes</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ clusterData.nodes?.total || 0 }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 5: Primary -->
|
<!-- Card 4: VMs -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="VMs"
|
||||||
<h6 class="card-title mb-0">
|
:value="clusterData.vms?.total || 0"
|
||||||
<span class="metric-label">Primary Node</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ clusterData.primary_node || 'N/A' }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 6: VMs -->
|
<!-- Card 5: Networks -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Networks"
|
||||||
<h6 class="card-title mb-0">
|
:value="clusterData.networks || 0"
|
||||||
<span class="metric-label">VMs</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ clusterData.vms?.total || 0 }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 7: OSDs -->
|
<!-- Card 6: OSDs -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="OSDs"
|
||||||
<h6 class="card-title mb-0">
|
:value="clusterData.osds?.total || 0"
|
||||||
<span class="metric-label">OSDs</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ clusterData.osds?.total || 0 }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 8: Pools -->
|
<!-- Card 7: Pools -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Pools"
|
||||||
<h6 class="card-title mb-0">
|
:value="clusterData.pools || 0"
|
||||||
<span class="metric-label">Pools</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ clusterData.pools || 0 }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 9: Volumes -->
|
<!-- Card 8: Volumes -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Volumes"
|
||||||
<h6 class="card-title mb-0">
|
:value="clusterData.volumes || 0"
|
||||||
<span class="metric-label">Volumes</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ clusterData.volumes || 0 }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="toggle-column expanded">
|
<div class="toggle-column expanded">
|
||||||
@ -333,6 +285,7 @@ import CPUChart from './charts/CPUChart.vue';
|
|||||||
import MemoryChart from './charts/MemoryChart.vue';
|
import MemoryChart from './charts/MemoryChart.vue';
|
||||||
import StorageChart from './charts/StorageChart.vue';
|
import StorageChart from './charts/StorageChart.vue';
|
||||||
import HealthChart from './charts/HealthChart.vue';
|
import HealthChart from './charts/HealthChart.vue';
|
||||||
|
import ValueCard from './ValueCard.vue';
|
||||||
|
|
||||||
// Register Chart.js components
|
// Register Chart.js components
|
||||||
ChartJS.register(
|
ChartJS.register(
|
||||||
|
91
pvc-vue/src/components/ValueCard.vue
Normal file
91
pvc-vue/src/components/ValueCard.vue
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<template>
|
||||||
|
<div class="metric-card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h6 class="card-title mb-0">
|
||||||
|
<span class="metric-label">{{ title }}</span>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="metric-value" :class="valueClass">{{ value }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: [String, Number],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
valueClass: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.metric-card {
|
||||||
|
min-width: 180px;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid rgba(0,0,0,0.125);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card .card-header {
|
||||||
|
background-color: rgba(0, 0, 0, 0.03);
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
|
||||||
|
height: 38px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header h6 {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card .card-body {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
overflow: visible;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-value {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-label {
|
||||||
|
color: #495057;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
</style>
|
@ -46,100 +46,52 @@
|
|||||||
<div class="section-content">
|
<div class="section-content">
|
||||||
<div class="metrics-row">
|
<div class="metrics-row">
|
||||||
<!-- Card 1: Daemon State -->
|
<!-- Card 1: Daemon State -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Daemon State"
|
||||||
<h6 class="card-title mb-0">
|
:value="selectedNodeData.daemon_state || 'Unknown'"
|
||||||
<span class="metric-label">Daemon State</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ selectedNodeData.daemon_state || 'Unknown' }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 2: Coordinator State -->
|
<!-- Card 2: Coordinator State -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Coordinator State"
|
||||||
<h6 class="card-title mb-0">
|
:value="selectedNodeData.coordinator_state || 'Unknown'"
|
||||||
<span class="metric-label">Coordinator State</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ selectedNodeData.coordinator_state || 'Unknown' }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 3: Domain State -->
|
<!-- Card 3: Domain State -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Domain State"
|
||||||
<h6 class="card-title mb-0">
|
:value="selectedNodeData.domain_state || 'Unknown'"
|
||||||
<span class="metric-label">Domain State</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ selectedNodeData.domain_state || 'Unknown' }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 4: Domains Count -->
|
<!-- Card 4: Domains Count -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Domains Count"
|
||||||
<h6 class="card-title mb-0">
|
:value="selectedNodeData.domains_count || 0"
|
||||||
<span class="metric-label">Domains Count</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ selectedNodeData.domains_count || 0 }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 5: PVC Version -->
|
<!-- Card 5: PVC Version -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="PVC Version"
|
||||||
<h6 class="card-title mb-0">
|
:value="selectedNodeData.pvc_version || 'Unknown'"
|
||||||
<span class="metric-label">PVC Version</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ selectedNodeData.pvc_version || 'Unknown' }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 6: Kernel Version -->
|
<!-- Card 6: Kernel Version -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Kernel Version"
|
||||||
<h6 class="card-title mb-0">
|
:value="selectedNodeData.kernel || 'Unknown'"
|
||||||
<span class="metric-label">Kernel Version</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ selectedNodeData.kernel || 'Unknown' }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 7: Host CPU Count -->
|
<!-- Card 7: Host CPU Count -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Host CPU Count"
|
||||||
<h6 class="card-title mb-0">
|
:value="selectedNodeData.cpu_count || 0"
|
||||||
<span class="metric-label">Host CPU Count</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ selectedNodeData.cpu_count || 0 }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card 8: Guest CPU Count -->
|
<!-- Card 8: Guest CPU Count -->
|
||||||
<div class="metric-card">
|
<ValueCard
|
||||||
<div class="card-header">
|
title="Guest CPU Count"
|
||||||
<h6 class="card-title mb-0">
|
:value="selectedNodeData.vcpu?.allocated || 0"
|
||||||
<span class="metric-label">Guest CPU Count</span>
|
/>
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="metric-value">{{ selectedNodeData.vcpu?.allocated || 0 }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="toggle-column expanded">
|
<div class="toggle-column expanded">
|
||||||
@ -305,10 +257,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, watch, nextTick, onUnmounted } from 'vue';
|
import { ref, computed, onMounted, watch, nextTick, onUnmounted } from 'vue';
|
||||||
import PageTitle from '../components/PageTitle.vue';
|
import PageTitle from '../components/PageTitle.vue';
|
||||||
|
import HealthChart from '../components/charts/HealthChart.vue';
|
||||||
import CPUChart from '../components/charts/CPUChart.vue';
|
import CPUChart from '../components/charts/CPUChart.vue';
|
||||||
import MemoryChart from '../components/charts/MemoryChart.vue';
|
import MemoryChart from '../components/charts/MemoryChart.vue';
|
||||||
import StorageChart from '../components/charts/StorageChart.vue';
|
import StorageChart from '../components/charts/StorageChart.vue';
|
||||||
import HealthChart from '../components/charts/HealthChart.vue';
|
import ValueCard from '../components/ValueCard.vue';
|
||||||
|
|
||||||
// Implement formatBytes function directly
|
// Implement formatBytes function directly
|
||||||
function formatBytes(bytes, decimals = 2) {
|
function formatBytes(bytes, decimals = 2) {
|
||||||
@ -359,13 +312,8 @@ const sections = ref({
|
|||||||
resources: true
|
resources: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Toggle section visibility
|
|
||||||
const toggleSection = (section) => {
|
|
||||||
sections.value[section] = !sections.value[section];
|
|
||||||
};
|
|
||||||
|
|
||||||
// State for selected node and tab scrolling
|
// State for selected node and tab scrolling
|
||||||
const selectedNode = ref('');
|
const selectedNode = ref(localStorage.getItem('selectedNodeTab') || '');
|
||||||
const tabsContainer = ref(null);
|
const tabsContainer = ref(null);
|
||||||
const showLeftScroll = ref(false);
|
const showLeftScroll = ref(false);
|
||||||
const showRightScroll = ref(false);
|
const showRightScroll = ref(false);
|
||||||
@ -379,83 +327,77 @@ const selectedNodeData = computed(() => {
|
|||||||
// Select a node
|
// Select a node
|
||||||
const selectNode = (nodeName) => {
|
const selectNode = (nodeName) => {
|
||||||
selectedNode.value = nodeName;
|
selectedNode.value = nodeName;
|
||||||
|
// Save to localStorage
|
||||||
|
localStorage.setItem('selectedNodeTab', nodeName);
|
||||||
|
|
||||||
// Scroll the selected tab into view
|
// Scroll the selected tab into view
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const activeTab = tabsContainer.value?.querySelector('.node-tab.active');
|
const activeTab = document.querySelector('.node-tab.active');
|
||||||
if (activeTab) {
|
if (activeTab) {
|
||||||
activeTab.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
|
activeTab.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Toggle section visibility
|
||||||
|
const toggleSection = (section) => {
|
||||||
|
sections.value[section] = !sections.value[section];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Scroll the tabs
|
||||||
|
const scrollTabs = (direction) => {
|
||||||
|
if (!tabsContainer.value) return;
|
||||||
|
|
||||||
|
const container = tabsContainer.value;
|
||||||
|
const scrollAmount = 200; // Adjust as needed
|
||||||
|
|
||||||
|
if (direction === 'left') {
|
||||||
|
container.scrollLeft -= scrollAmount;
|
||||||
|
} else {
|
||||||
|
container.scrollLeft += scrollAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if scroll buttons should be shown
|
// Update scroll button visibility after scrolling
|
||||||
checkScrollButtons();
|
checkScrollButtons();
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if scroll buttons should be shown
|
// Check if scroll buttons should be shown
|
||||||
const checkScrollButtons = () => {
|
const checkScrollButtons = () => {
|
||||||
if (!tabsContainer.value) return;
|
if (!tabsContainer.value) return;
|
||||||
|
|
||||||
const { scrollLeft, scrollWidth, clientWidth } = tabsContainer.value;
|
const container = tabsContainer.value;
|
||||||
|
showLeftScroll.value = container.scrollLeft > 0;
|
||||||
// Show left scroll button if not at the beginning
|
showRightScroll.value = container.scrollLeft < (container.scrollWidth - container.clientWidth - 5); // 5px buffer
|
||||||
showLeftScroll.value = scrollLeft > 0;
|
|
||||||
|
|
||||||
// Show right scroll button if not at the end
|
|
||||||
showRightScroll.value = scrollLeft + clientWidth < scrollWidth - 1; // -1 for rounding errors
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Scroll tabs left or right
|
// Calculate CPU utilization
|
||||||
const scrollTabs = (direction) => {
|
const calculateCpuUtilization = (node) => {
|
||||||
if (!tabsContainer.value) return;
|
if (!node || !node.load || !node.cpu_count) return 0;
|
||||||
|
return Math.round((node.load / node.cpu_count) * 100);
|
||||||
const scrollAmount = tabsContainer.value.clientWidth / 2;
|
|
||||||
const currentScroll = tabsContainer.value.scrollLeft;
|
|
||||||
|
|
||||||
if (direction === 'left') {
|
|
||||||
tabsContainer.value.scrollTo({
|
|
||||||
left: Math.max(0, currentScroll - scrollAmount),
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
tabsContainer.value.scrollTo({
|
|
||||||
left: currentScroll + scrollAmount,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check scroll buttons after scrolling
|
|
||||||
setTimeout(checkScrollButtons, 300);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate CPU utilization (load / cpu_count * 100)
|
// Calculate memory utilization
|
||||||
const calculateCpuUtilization = (nodeData) => {
|
const calculateMemoryUtilization = (node) => {
|
||||||
if (!nodeData || !nodeData.load || !nodeData.cpu_count) return 0;
|
if (!node || !node.memory) return 0;
|
||||||
|
const used = node.memory.used || 0;
|
||||||
const utilization = (nodeData.load / nodeData.cpu_count) * 100;
|
const total = node.memory.total || 1; // Avoid division by zero
|
||||||
return Math.round(utilization);
|
if (total === 0) return 0;
|
||||||
|
return Math.round((node.memory.used / node.memory.total) * 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate memory utilization (memory.used / memory.total * 100)
|
// Calculate allocated memory
|
||||||
const calculateMemoryUtilization = (nodeData) => {
|
const calculateAllocatedMemory = (node) => {
|
||||||
if (!nodeData || !nodeData.memory || !nodeData.memory.used || !nodeData.memory.total) return 0;
|
if (!node || !node.memory) return 0;
|
||||||
|
const allocated = node.memory.allocated || 0;
|
||||||
const utilization = (nodeData.memory.used / nodeData.memory.total) * 100;
|
const total = node.memory.total || 1; // Avoid division by zero
|
||||||
return Math.round(utilization);
|
if (total === 0) return 0;
|
||||||
};
|
return Math.round((node.memory.allocated / node.memory.total) * 100);
|
||||||
|
|
||||||
// Calculate allocated memory (memory.allocated / memory.total * 100)
|
|
||||||
const calculateAllocatedMemory = (nodeData) => {
|
|
||||||
if (!nodeData || !nodeData.memory || !nodeData.memory.allocated || !nodeData.memory.total) return 0;
|
|
||||||
|
|
||||||
const utilization = (nodeData.memory.allocated / nodeData.memory.total) * 100;
|
|
||||||
return Math.round(utilization);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prepare chart data for the node
|
// Prepare chart data for the node
|
||||||
const nodeHealthChartData = computed(() => {
|
const nodeHealthChartData = computed(() => {
|
||||||
// Get node metrics history if available
|
// Get node metrics history if available
|
||||||
const nodeMetrics = props.metricsData.nodes?.[selectedNodeData.value.name];
|
const nodeMetrics = props.metricsData.nodes?.[selectedNodeData.value?.name];
|
||||||
|
|
||||||
if (nodeMetrics && nodeMetrics.health && nodeMetrics.health.data.length > 0) {
|
if (nodeMetrics && nodeMetrics.health && nodeMetrics.health.data.length > 0) {
|
||||||
return {
|
return {
|
||||||
@ -467,13 +409,13 @@ const nodeHealthChartData = computed(() => {
|
|||||||
// Fallback to current value only
|
// Fallback to current value only
|
||||||
return {
|
return {
|
||||||
labels: ['Health'],
|
labels: ['Health'],
|
||||||
data: [selectedNodeData.value.health || 0]
|
data: [selectedNodeData.value?.health || 0]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const nodeCpuChartData = computed(() => {
|
const nodeCpuChartData = computed(() => {
|
||||||
// Get node metrics history if available
|
// Get node metrics history if available
|
||||||
const nodeMetrics = props.metricsData.nodes?.[selectedNodeData.value.name];
|
const nodeMetrics = props.metricsData.nodes?.[selectedNodeData.value?.name];
|
||||||
|
|
||||||
if (nodeMetrics && nodeMetrics.cpu && nodeMetrics.cpu.data.length > 0) {
|
if (nodeMetrics && nodeMetrics.cpu && nodeMetrics.cpu.data.length > 0) {
|
||||||
return {
|
return {
|
||||||
@ -531,8 +473,15 @@ const nodeAllocatedMemoryChartData = computed(() => {
|
|||||||
|
|
||||||
// Initialize the component
|
// Initialize the component
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// Select the first node by default if available
|
// Check if the saved node exists in the current node data
|
||||||
if (props.nodeData && props.nodeData.length > 0) {
|
const savedNode = localStorage.getItem('selectedNodeTab');
|
||||||
|
const nodeExists = props.nodeData.some(node => node.name === savedNode);
|
||||||
|
|
||||||
|
if (savedNode && nodeExists) {
|
||||||
|
// Use the saved node
|
||||||
|
selectedNode.value = savedNode;
|
||||||
|
} else if (props.nodeData && props.nodeData.length > 0) {
|
||||||
|
// Default to the first node if saved node doesn't exist
|
||||||
selectNode(props.nodeData[0].name);
|
selectNode(props.nodeData[0].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,9 +491,10 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Watch for changes in the nodes data
|
// Watch for changes in the nodes data
|
||||||
watch(() => props.nodeData, () => {
|
watch(() => props.nodeData, (newNodes) => {
|
||||||
// If the currently selected node no longer exists, select the first available node
|
// If the currently selected node no longer exists, select the first available node
|
||||||
if (props.nodeData && props.nodeData.length > 0 && !props.nodeData.find(node => node.name === selectedNode.value)) {
|
const nodeExists = newNodes.some(node => node.name === selectedNode.value);
|
||||||
|
if (newNodes && newNodes.length > 0 && !nodeExists) {
|
||||||
selectNode(props.nodeData[0].name);
|
selectNode(props.nodeData[0].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user